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 offset = (page - 1) * limit; const parameter = ['q=*:*', `rows=${limit}`, `start=${offset}`]; const solrUrl = process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&'); console.log('[SOLR QUERY SITEMAP]', solrUrl); const result = await axios(solrUrl); 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); } // --- 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; } // 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 || '*'}]` ); } 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})`); } const solrUrl = process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&'); console.log('[SOLR QUERY NORMAL]', solrUrl); const result = await axios(solrUrl); result.data.response.products = productMappingSolr( result.data.response.docs, false ); 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 }); } }