summaryrefslogtreecommitdiff
path: root/src/pages/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/pages/api')
-rw-r--r--src/pages/api/shop/search.js230
-rw-r--r--src/pages/api/shop/searchkey.js43
2 files changed, 208 insertions, 65 deletions
diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js
index 89d75cd0..1f636f28 100644
--- a/src/pages/api/shop/search.js
+++ b/src/pages/api/shop/search.js
@@ -7,7 +7,11 @@ const escapeSolrQuery = (query) => {
query = query.replace(/-/g, ' ');
const specialChars = /([\+\!\(\)\{\}\[\]\^"~\*\?:\\\/])/g;
const words = query.split(/\s+/);
- return words.map((word) => specialChars.test(word) ? word.replace(specialChars, '\\$1') : word).join(' ');
+ return words
+ .map((word) =>
+ specialChars.test(word) ? word.replace(specialChars, '\\$1') : word,
+ )
+ .join(' ');
};
export default async function handler(req, res) {
@@ -23,7 +27,7 @@ export default async function handler(req, res) {
fq = '', // bisa berupa string atau array
limit = 30,
source = '',
- group = 'true',
+ group = 'true',
} = req.query;
let { stock = '' } = req.query;
@@ -37,12 +41,15 @@ export default async function handler(req, res) {
if (qCompare !== '*:*') {
qCompare = escapeSolrQuery(qCompare);
- qCompare = qCompare.split(/\s+/).map(term => {
- if (term && !term.includes('*')) {
- return term + '*';
- }
- return term;
- }).join(' ');
+ qCompare = qCompare
+ .split(/\s+/)
+ .map((term) => {
+ if (term && !term.includes('*')) {
+ return term + '*';
+ }
+ return term;
+ })
+ .join(' ');
}
// [SOLUSI] Gunakan URLSearchParams untuk menyusun URL dengan aman
@@ -52,17 +59,20 @@ export default async function handler(req, res) {
params.append('rows', limit);
params.append('wt', 'json');
params.append('indent', 'true');
-
+
// Gunakan eDisMax parser (Otak Cerdas)
params.append('defType', 'edismax');
-
+
// Set Prioritas Pencarian (Boost ^)
// 1. default_code_s^20 : SKU persis (Prioritas Tertinggi)
// 2. search_keywords_t^10 : Field baru (Case insensitive)
// 3. display_name_s^1 : Cadangan
- params.append('qf', 'default_code_s^20 search_keywords_t^10 display_name_s^1');
-
- const compareWords = qCompare.split(/\s+/).filter(w => w.length > 0);
+ params.append(
+ 'qf',
+ 'default_code_s^20 search_keywords_t^10 display_name_s^1',
+ );
+
+ const compareWords = qCompare.split(/\s+/).filter((w) => w.length > 0);
let compareMm = '100%';
if (compareWords.length >= 3) {
compareMm = '75%';
@@ -79,7 +89,10 @@ export default async function handler(req, res) {
}
// Field List (fl)
- params.append('fl', 'id,display_name_s,default_code_s,image_s,price_tier1_v2_f,attribute_set_id_i,attribute_set_name_s,template_id_i,product_id_i');
+ params.append(
+ 'fl',
+ 'id,display_name_s,default_code_s,image_s,price_tier1_v2_f,attribute_set_id_i,attribute_set_name_s,template_id_i,product_id_i',
+ );
// Filter Query (fq) Dasar
params.append('fq', '-publish_b:false');
@@ -88,7 +101,7 @@ export default async function handler(req, res) {
// Logic Locking (Filter Attribute Set ID dari Frontend)
if (fq) {
if (Array.isArray(fq)) {
- fq.forEach(f => params.append('fq', f));
+ fq.forEach((f) => params.append('fq', f));
} else {
params.append('fq', fq);
}
@@ -97,34 +110,36 @@ export default async function handler(req, res) {
// Target Core: VARIANTS
// HAPUS parameter manual dari string URL, gunakan params object
const solrUrl = process.env.SOLR_HOST + '/solr/variants/select';
-
+
// Axios akan otomatis handle encoding % dan & dengan benar
const result = await axios.get(solrUrl, { params: params });
// Mapping Result
const mappedProducts = productMappingSolr(
result.data.response.docs,
- false
+ false,
);
const finalResponse = {
...result.data,
response: {
...result.data.response,
- products: mappedProducts
- }
+ products: mappedProducts,
+ },
};
delete finalResponse.response.docs;
const camelCasedData = camelcaseObjectDeep(finalResponse);
return res.status(200).json(camelCasedData);
-
} catch (e) {
console.error('[COMPARE SEARCH ERROR]', e.message);
if (e.response && e.response.data) {
// Log detail error dari Solr
- console.error('[SOLR DETAILS]:', JSON.stringify(e.response.data, null, 2));
+ console.error(
+ '[SOLR DETAILS]:',
+ JSON.stringify(e.response.data, null, 2),
+ );
}
return res.status(200).json({ response: { products: [], numFound: 0 } });
}
@@ -149,49 +164,49 @@ export default async function handler(req, res) {
// Filter Query khusus Upsell
`fq=${encodeURIComponent(fqUpsell)}`,
// Tetap filter yang publish & ada harga agar produk valid
- `fq=${encodeURIComponent('-publish_b:false')}`,
- `fq=${encodeURIComponent('price_tier1_v2_f:[1 TO *]')}`
+ `fq=${encodeURIComponent('-publish_b:false')}`,
+ `fq=${encodeURIComponent('price_tier1_v2_f:[1 TO *]')}`,
];
// PENTING: SEARCH DI CORE 'VARIANTS'
- const solrUrl = process.env.SOLR_HOST + '/solr/variants/select?' + parameter.join('&');
+ const solrUrl =
+ process.env.SOLR_HOST + '/solr/variants/select?' + parameter.join('&');
const result = await axios(solrUrl);
// 1. Mapping dasar
const mappedProducts = productMappingSolr(
result.data.response.docs,
- false
+ false,
);
// 2. FIX URL LINK: Override ID Varian dengan Template ID
const rawDocs = result.data.response.docs;
-
+
const fixedProducts = mappedProducts.map((p, index) => {
- const raw = rawDocs[index];
- if (raw && raw.template_id_i) {
- return {
- ...p,
- id: raw.template_id_i, // Ganti ID Varian jadi ID Template agar link valid
- variantId: raw.product_id_i
- };
- }
- return p;
+ const raw = rawDocs[index];
+ if (raw && raw.template_id_i) {
+ return {
+ ...p,
+ id: raw.template_id_i, // Ganti ID Varian jadi ID Template agar link valid
+ variantId: raw.product_id_i,
+ };
+ }
+ return p;
});
const finalResponse = {
- ...result.data,
- response: {
- ...result.data.response,
- products: fixedProducts
- }
+ ...result.data,
+ response: {
+ ...result.data.response,
+ products: fixedProducts,
+ },
};
delete finalResponse.response.docs;
const camelCasedData = camelcaseObjectDeep(finalResponse);
return res.status(200).json(camelCasedData);
-
} catch (e) {
console.error('[UPSELL ERROR]', e.response?.data || e.message);
return res.status(200).json({ response: { products: [], numFound: 0 } });
@@ -212,9 +227,13 @@ export default async function handler(req, res) {
'wt=json',
'omitHeader=true',
];
- const solrUrl = process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&');
+ const solrUrl =
+ process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&');
const result = await axios(solrUrl, { timeout: 25000 });
- result.data.response.products = productMappingSolr(result.data.response.docs, false);
+ result.data.response.products = productMappingSolr(
+ result.data.response.docs,
+ false,
+ );
delete result.data.response.docs;
result.data = camelcaseObjectDeep(result.data);
return res.status(200).json(result.data);
@@ -229,21 +248,45 @@ export default async function handler(req, res) {
let paramOrderBy = '';
switch (orderBy) {
- case 'flashsale-discount-desc': paramOrderBy += 'flashsale_discount_f DESC'; break;
- case 'price-asc': paramOrderBy += 'price_tier1_v2_f ASC'; break;
- case 'price-desc': paramOrderBy += 'price_tier1_v2_f DESC'; break;
- case 'popular': paramOrderBy += 'product_rating_f DESC, search_rank_i DESC,'; break;
- case 'popular-weekly': paramOrderBy += 'search_rank_weekly_i DESC'; break;
- case 'stock': paramOrderBy += 'product_rating_f DESC, stock_total_f DESC'; break;
- case 'flashsale-price-asc': paramOrderBy += 'flashsale_price_f ASC'; break;
- default: paramOrderBy += ''; break;
+ case 'flashsale-discount-desc':
+ paramOrderBy += 'flashsale_discount_f DESC';
+ break;
+ case 'price-asc':
+ paramOrderBy += 'price_tier1_v2_f ASC';
+ break;
+ case 'price-desc':
+ paramOrderBy += 'price_tier1_v2_f DESC';
+ break;
+ case 'popular':
+ paramOrderBy += 'product_rating_f DESC, search_rank_i DESC,';
+ break;
+ case 'popular-weekly':
+ paramOrderBy += 'search_rank_weekly_i DESC';
+ break;
+ case 'stock':
+ paramOrderBy += 'product_rating_f DESC, stock_total_f DESC';
+ break;
+ case 'flashsale-price-asc':
+ paramOrderBy += 'flashsale_price_f ASC';
+ break;
+ default:
+ paramOrderBy += '';
+ break;
}
let checkQ = q.trim().split(/[\s\+\-\!\(\)\{\}\[\]\^"~\*\?:\\\/]+/);
let newQ = escapeSolrQuery(q);
- const formattedQuery = `(${newQ.split(' ').map((term) => (term.length < 2 ? term : `${term}*`)).join(' ')})`;
- const mm = checkQ.length > 2 ? (checkQ.length > 5 ? '55%' : '85%') : `${checkQ.length}`;
+ const formattedQuery = `(${newQ
+ .split(' ')
+ .map((term) => (term.length < 2 ? term : `${term}*`))
+ .join(' ')})`;
+ const mm =
+ checkQ.length > 2
+ ? checkQ.length > 5
+ ? '55%'
+ : '85%'
+ : `${checkQ.length}`;
const filterQueries = [
'-publish_b:false',
@@ -256,8 +299,8 @@ export default async function handler(req, res) {
// Handle 'fq' parameter from request
let finalFq = [...filterQueries];
if (fq) {
- if (Array.isArray(fq)) finalFq.push(...fq);
- else finalFq.push(fq);
+ if (Array.isArray(fq)) finalFq.push(...fq);
+ else finalFq.push(fq);
}
let keywords = newQ;
@@ -287,12 +330,14 @@ export default async function handler(req, res) {
];
// Masukkan semua Filter Query (fq)
- finalFq.forEach(f => {
- parameter.push(`fq=${encodeURIComponent(f)}`);
+ finalFq.forEach((f) => {
+ parameter.push(`fq=${encodeURIComponent(f)}`);
});
if (priceFrom > 0 || priceTo > 0) {
- parameter.push(`fq=price_tier1_v2_f:[${priceFrom || '*'} TO ${priceTo || '*'}]`);
+ parameter.push(
+ `fq=price_tier1_v2_f:[${priceFrom || '*'} TO ${priceTo || '*'}]`,
+ );
}
let { auth } = req.cookies;
@@ -302,32 +347,87 @@ export default async function handler(req, res) {
}
if (brand) {
- const brandExpr = brand.split(',').map(m => `manufacture_name:"${encodeURIComponent(m)}"`).join(' OR ');
+ const brandExpr = brand
+ .split(',')
+ .map((m) => `manufacture_name:"${encodeURIComponent(m)}"`)
+ .join(' OR ');
parameter.push(`fq={!tag=brand}(${brandExpr})`);
}
if (category) {
- const catExpr = category.split(',').map(c => `category_name:"${encodeURIComponent(c)}"`).join(' OR ');
+ const catExpr = category
+ .split(',')
+ .map((c) => `category_name:"${encodeURIComponent(c)}"`)
+ .join(' OR ');
parameter.push(`fq={!tag=cat}(${catExpr})`);
}
if (stock) parameter.push(`fq=stock_total_f:(1 TO *)`);
+ if (typeof fq === 'string') parameter.push(`fq=${encodeURIComponent(fq)}`);
+
+ if (Array.isArray(fq))
+ parameter = parameter.concat(
+ fq.map((val) => `fq=${encodeURIComponent(val)}`),
+ );
+
+ // Searchkey
+ if (req.query.from === 'searchkey') {
+ const ids = req.query.ids ? req.query.ids.split(',').filter(Boolean) : [];
+
+ const q = ids.map((id) => `product_id_i:${id}`).join(' OR ');
+
+ const strictQuery = [
+ `q=${encodeURIComponent(q)}`,
+ `fq=-publish_b:false AND price_tier1_v2_f:[1 TO *] AND product_rating_f:[8 TO *]`,
+ // `qf=variants_code_t variants_name_t`,
+ `rows=${limit}`,
+ `start=${offset}`,
+ `sort=${paramOrderBy}`,
+ ];
+
+ const solrUrl =
+ process.env.SOLR_HOST + '/solr/product/select?' + strictQuery.join('&');
+
+ console.log('[SEARCHKEY FINAL QUERY]', solrUrl);
+
+ const result = await axios(solrUrl);
+
+ try {
+ result.data.response.products = productMappingSolr(
+ result.data.response.docs,
+ auth?.pricelist || false,
+ );
+
+ delete result.data.response.docs;
+ result.data = camelcaseObjectDeep(result.data);
+
+ return res.status(200).json(result.data);
+ } catch (e) {
+ return res.status(400).json({ error: e.message });
+ }
+ }
+
// SEARCH NORMAL: DEFAULT KE CORE 'PRODUCT'
- const solrUrl = process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&');
+ const solrUrl =
+ process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&');
try {
const result = await axios(solrUrl);
result.data.response.products = productMappingSolr(
result.data.response.docs,
- auth?.pricelist || false
+ auth?.pricelist || false,
+ );
+ result.data.responseHeader.params.start = parseInt(
+ result.data.responseHeader.params.start,
+ );
+ result.data.responseHeader.params.rows = parseInt(
+ result.data.responseHeader.params.rows,
);
- result.data.responseHeader.params.start = parseInt(result.data.responseHeader.params.start);
- result.data.responseHeader.params.rows = parseInt(result.data.responseHeader.params.rows);
delete result.data.response.docs;
result.data = camelcaseObjectDeep(result.data);
res.status(200).json(result.data);
} catch (error) {
res.status(400).json({ error: error.message });
}
-} \ No newline at end of file
+}
diff --git a/src/pages/api/shop/searchkey.js b/src/pages/api/shop/searchkey.js
new file mode 100644
index 00000000..f5546a36
--- /dev/null
+++ b/src/pages/api/shop/searchkey.js
@@ -0,0 +1,43 @@
+import axios from 'axios';
+
+export default async function handler(req, res) {
+ const { url = '', page = 1, limit = 30, all } = req.query;
+
+ let q = '*:*';
+
+ // ✅ kalau BUKAN sitemap
+ if (!all) {
+ const cleanUrl = url.trim();
+ if (!cleanUrl) {
+ return res.status(400).json({ error: 'Missing url param' });
+ }
+ q = `keywords_s:"${cleanUrl}"`;
+ }
+
+ const offset = (page - 1) * limit;
+
+ const params = [
+ `q.op=AND`,
+ `q=${q}`,
+ `indent=true`,
+ `rows=${limit}`,
+ `start=${offset}`,
+ ];
+
+ try {
+ const result = await axios.post(
+ `${process.env.SOLR_HOST}/solr/searchkey/select`,
+ params.join('&'),
+ {
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ }
+ );
+
+ res.status(200).json(result.data);
+ } catch (error) {
+ console.error(error?.response?.data || error);
+ res.status(500).json({ error: 'Internal Server Error' });
+ }
+}