summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMiqdad <ahmadmiqdad27@gmail.com>2025-11-13 18:54:22 +0700
committerMiqdad <ahmadmiqdad27@gmail.com>2025-11-13 18:54:22 +0700
commit8d2486107d75e464135adbfc7ad113e41dbc764f (patch)
tree869376e3b0f79bb8bb19ee694fc44885eb9ab8ed /src
parent74a38d9794e0c4ec937981a45782ab9c80ebf9ef (diff)
<MIqdad> fix sitemap
Diffstat (limited to 'src')
-rw-r--r--src/pages/api/shop/search.js337
1 files changed, 198 insertions, 139 deletions
diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js
index 1b1b6a9c..208f511c 100644
--- a/src/pages/api/shop/search.js
+++ b/src/pages/api/shop/search.js
@@ -2,178 +2,237 @@ import { productMappingSolr } from '@/utils/solrMapping';
import axios from 'axios';
import camelcaseObjectDeep from 'camelcase-object-deep';
-// helper untuk escape karakter spesial
-const escapeSolrQuery = (query) => {
- if (query == '*') return query;
- query = query.replace(/-/g, ' ');
- const specialChars = /([\+\!\(\)\{\}\[\]\^"~\*\?:\\\/])/g;
- const words = query.split(/\s+/);
- const escapedWords = words.map((word) => {
- if (specialChars.test(word)) {
- return word.replace(specialChars, '\\$1');
- }
- return word;
- });
- return escapedWords.join(' ');
-};
-
export default async function handler(req, res) {
- try {
- const {
- q = '*',
- page = 1,
- brand = '',
- category = '',
- priceFrom = 0,
- priceTo = 0,
- orderBy = '',
- operation = 'AND',
- fq = '',
- limit = 30,
- source = '', // <-- penting
- } = req.query;
-
- console.log('🔍 API /shop/search → source:', source); // debug biar tahu kebaca atau enggak
-
- // --- 1️⃣ MODE SITEMAP: ambil semua produk tanpa filter ---
- if (source === 'sitemap') {
+ const {
+ q = '*',
+ page = 1,
+ brand = '',
+ category = '',
+ priceFrom = 0,
+ priceTo = 0,
+ orderBy = '',
+ operation = 'AND',
+ fq = '',
+ limit = 30,
+ source = '',
+ } = req.query;
+
+ let { stock = '' } = req.query;
+
+ // ============================================================
+ // 🚀 1. MODE SITEMAP → SOLR q=*:* TANPA FILTER / FACET / SORT
+ // ============================================================
+ if (source === 'sitemap') {
+ try {
const offset = (page - 1) * limit;
- const parameter = ['q=*:*', `rows=${limit}`, `start=${offset}`];
+
+ // Query super ringan
+ const parameter = [
+ 'q=*:*',
+ `rows=${limit}`,
+ `start=${offset}`,
+ // biar ringan — ambil field penting saja
+ 'fl=product_id_i,name_s,default_code_s,image_s,category_name',
+ 'wt=json',
+ 'omitHeader=true',
+ ];
const solrUrl =
process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&');
- console.log('[SOLR QUERY SITEMAP]', solrUrl);
+ console.log('[SITEMAP SOLR QUERY]', solrUrl);
- const result = await axios(solrUrl);
+ const result = await axios(solrUrl, { timeout: 25000 });
+ // mapping seperti biasa
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);
+ } catch (e) {
+ console.error('[SITEMAP ERROR]', e);
+ return res.status(500).json({ error: 'Sitemap query failed' });
}
+ }
- // --- 2️⃣ MODE NORMAL (search biasa di website) ---
- 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}`;
-
- // filter default (mode normal)
- const filterQueries = [
- '-publish_b:false',
- 'product_rating_f:[8 TO *]',
- 'price_tier1_v2_f:[1 TO *]',
- ];
- const fq_ = filterQueries.join(' AND ');
-
- let keywords = newQ;
- if (checkQ.length >= 3) keywords = formattedQuery;
-
- let offset = (page - 1) * limit;
-
- 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;
- }
+ // ============================================================
+ // 🚀 2. MODE SEARCH NORMAL — TIDAK DIUBAH SAMA SEKALI
+ // ============================================================
+
+ 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;
+ }
- // parameter query normal
- let parameter = [
- 'facet.field={!ex=brand}manufacture_name_s',
- 'facet.field={!ex=cat}category_name',
- 'facet=true',
- 'indent=true',
- `facet.query=${escapeSolrQuery(q)}`,
- `q.op=${operation}`,
- `q=${keywords}`,
- `defType=edismax`,
- 'qf=name_s description_clean_t category_name manufacture_name_s variants_code_t variants_name_t category_id_ids default_code_s manufacture_id_i category_id_i',
- `start=${parseInt(offset)}`,
- `rows=${limit}`,
- `sort=${paramOrderBy}`,
- `fq=${encodeURIComponent(fq_)}`,
- `mm=${encodeURIComponent(mm)}`,
- ];
-
- if (priceFrom > 0 || priceTo > 0) {
- parameter.push(
- `fq=price_tier1_v2_f:[${priceFrom || '*'} TO ${priceTo || '*'}]`
- );
- }
+ 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 filterQueries = [
+ '-publish_b:false',
+ 'product_rating_f:[8 TO *]',
+ 'price_tier1_v2_f:[1 TO *]',
+ ];
+
+ if (orderBy === 'stock') {
+ filterQueries.push('stock_total_f:[1 TO *]');
+ }
- if (brand) {
- const brandExpr = brand
- .split(',')
- .map(
- (manufacturer) =>
- `manufacture_name:"${encodeURIComponent(manufacturer)}"`
- )
- .join(' OR ');
- parameter.push(`fq={!tag=brand}(${brandExpr})`);
- }
+ if (fq && source != 'similar' && typeof fq != 'string') {
+ fq.push(...filterQueries);
+ }
- if (category) {
- const catExpr = category
- .split(',')
- .map((cat) => `category_name:"${encodeURIComponent(cat)}"`)
- .join(' OR ');
- parameter.push(`fq={!tag=cat}(${catExpr})`);
+ const fq_ = filterQueries.join(' AND ');
+
+ let keywords = newQ;
+ if (source === 'similar' || checkQ.length < 3) {
+ if (checkQ.length < 2 || checkQ[1].length < 2) {
+ keywords = newQ;
+ } else {
+ keywords = newQ + '*';
}
+ } else {
+ keywords = formattedQuery;
+ }
- const solrUrl =
- process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&');
+ let offset = (page - 1) * limit;
+
+ let parameter = [
+ 'facet.field={!ex=brand}manufacture_name_s',
+ 'facet.field={!ex=cat}category_name',
+ 'facet=true',
+ 'indent=true',
+ `facet.query=${escapeSolrQuery(q)}`,
+ `q.op=OR`,
+ `q=${keywords}`,
+ `defType=edismax`,
+ 'qf=name_s description_clean_t category_name manufacture_name_s variants_code_t variants_name_t category_id_ids default_code_s manufacture_id_i category_id_i',
+ `start=${parseInt(offset)}`,
+ `rows=${limit}`,
+ `sort=${paramOrderBy}`,
+ `fq=${encodeURIComponent(fq_)}`,
+ `mm=${encodeURIComponent(mm)}`,
+ ];
+
+ if (priceFrom > 0 || priceTo > 0) {
+ parameter.push(
+ `fq=price_tier1_v2_f:[${priceFrom == '' ? '*' : priceFrom} TO ${
+ priceTo == '' ? '*' : priceTo
+ }]`
+ );
+ }
- console.log('[SOLR QUERY NORMAL]', solrUrl);
+ let { auth } = req.cookies;
+ if (auth) {
+ auth = JSON.parse(auth);
+ if (auth.feature.onlyReadyStock) stock = true;
+ }
- const result = await axios(solrUrl);
+ if (brand) {
+ const brandExpr = brand
+ .split(',')
+ .map(
+ (manufacturer) =>
+ `manufacture_name:"${encodeURIComponent(manufacturer)}"`
+ )
+ .join(' OR ');
+ parameter.push(`fq={!tag=brand}(${brandExpr})`);
+ }
+
+ if (category) {
+ const catExpr = category
+ .split(',')
+ .map((cat) => `category_name:"${encodeURIComponent(cat)}"`)
+ .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)}`)
+ );
+
+ const solrUrl =
+ process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&');
+ const result = await axios(solrUrl);
+
+ try {
result.data.response.products = productMappingSolr(
result.data.response.docs,
- 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
);
delete result.data.response.docs;
result.data = camelcaseObjectDeep(result.data);
-
res.status(200).json(result.data);
} catch (error) {
- console.error('[ERROR /shop/search]', error.message);
res.status(400).json({ error: error.message });
}
}
+
+const escapeSolrQuery = (query) => {
+ if (query == '*') return query;
+
+ query = query.replace(/-/g, ' ');
+
+ const specialChars = /([\+\!\(\)\{\}\[\]\^"~\*\?:\\\/])/g;
+ const words = query.split(/\s+/);
+ const escapedWords = words.map((word) => {
+ if (specialChars.test(word)) {
+ return word.replace(specialChars, '\\$1');
+ }
+ return word;
+ });
+
+ return escapedWords.join(' ');
+};