app.js 7.5 KB
// SearchEngine Frontend JavaScript

// API endpoint
const API_BASE_URL = 'http://localhost:6002';

// Update API URL display
document.getElementById('apiUrl').textContent = API_BASE_URL;

// Handle Enter key in search input
function handleKeyPress(event) {
    if (event.key === 'Enter') {
        performSearch();
    }
}

// Set query from example buttons
function setQuery(query) {
    document.getElementById('searchInput').value = query;
    performSearch();
}

// Perform search
async function performSearch() {
    const query = document.getElementById('searchInput').value.trim();

    if (!query) {
        alert('请输入搜索关键词');
        return;
    }

    // Get options
    const size = parseInt(document.getElementById('resultSize').value);
    const enableTranslation = document.getElementById('enableTranslation').checked;
    const enableEmbedding = document.getElementById('enableEmbedding').checked;
    const enableRerank = document.getElementById('enableRerank').checked;

    // Show loading
    document.getElementById('loading').style.display = 'block';
    document.getElementById('results').innerHTML = '';
    document.getElementById('queryInfo').innerHTML = '';

    try {
        const response = await fetch(`${API_BASE_URL}/search/`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                query: query,
                size: size,
                enable_translation: enableTranslation,
                enable_embedding: enableEmbedding,
                enable_rerank: enableRerank
            })
        });

        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }

        const data = await response.json();
        displayResults(data);
        displayQueryInfo(data.query_info);

    } catch (error) {
        console.error('Search error:', error);
        document.getElementById('results').innerHTML = `
            <div class="error-message">
                <strong>搜索出错:</strong> ${error.message}
                <br><br>
                <small>请确保后端服务正在运行 (http://localhost:6002)</small>
            </div>
        `;
    } finally {
        document.getElementById('loading').style.display = 'none';
    }
}

// Display search results
function displayResults(data) {
    const resultsDiv = document.getElementById('results');

    if (!data.hits || data.hits.length === 0) {
        resultsDiv.innerHTML = `
            <div class="no-results">
                <h3>😔 没有找到结果</h3>
                <p>请尝试其他关键词</p>
            </div>
        `;
        return;
    }

    let html = `
        <div class="results-header">
            <h2>搜索结果</h2>
            <div class="results-stats">
                找到 <strong>${data.total}</strong> 个结果
                耗时 <strong>${data.took_ms}</strong> 毫秒
                最高分 <strong>${data.max_score.toFixed(4)}</strong>
            </div>
        </div>
    `;

    data.hits.forEach((hit, index) => {
        const source = hit._source;
        const score = hit._custom_score || hit._score;

        html += `
            <div class="result-item">
                <div class="result-header">
                    <div>
                        <div class="result-title">${index + 1}. ${escapeHtml(source.name || 'N/A')}</div>
                        ${source.enSpuName ? `<div style="color: #666; font-size: 14px;">${escapeHtml(source.enSpuName)}</div>` : ''}
                        ${source.ruSkuName ? `<div style="color: #999; font-size: 13px;">${escapeHtml(source.ruSkuName)}</div>` : ''}
                    </div>
                    <div class="result-score">
                        ${score.toFixed(4)}
                    </div>
                </div>

                <div class="result-meta">
                    ${source.categoryName ? `<span>📁 ${escapeHtml(source.categoryName)}</span>` : ''}
                    ${source.brandName ? `<span>🏷️ ${escapeHtml(source.brandName)}</span>` : ''}
                    ${source.supplierName ? `<span>🏭 ${escapeHtml(source.supplierName)}</span>` : ''}
                    ${source.create_time ? `<span>📅 ${formatDate(source.create_time)}</span>` : ''}
                </div>

                ${source.imageUrl ? `
                    <img src="${escapeHtml(source.imageUrl)}"
                         alt="${escapeHtml(source.name)}"
                         class="result-image"
                         onerror="this.style.display='none'">
                ` : ''}

                <div style="margin-top: 10px; font-size: 12px; color: #999;">
                    ID: ${source.skuId || 'N/A'}
                </div>
            </div>
        `;
    });

    resultsDiv.innerHTML = html;
}

// Display query processing information
function displayQueryInfo(queryInfo) {
    if (!queryInfo) return;

    const queryInfoDiv = document.getElementById('queryInfo');

    let html = `
        <h3>查询处理信息</h3>
        <div class="info-grid">
            <div class="info-item">
                <strong>原始查询</strong>
                ${escapeHtml(queryInfo.original_query || 'N/A')}
            </div>
            <div class="info-item">
                <strong>重写后查询</strong>
                ${escapeHtml(queryInfo.rewritten_query || 'N/A')}
            </div>
            <div class="info-item">
                <strong>检测语言</strong>
                ${getLanguageName(queryInfo.detected_language)}
            </div>
            <div class="info-item">
                <strong>查询域</strong>
                ${escapeHtml(queryInfo.domain || 'default')}
            </div>
        </div>
    `;

    // Show translations if any
    if (queryInfo.translations && Object.keys(queryInfo.translations).length > 0) {
        html += '<h4 style="margin-top: 20px; margin-bottom: 10px;">翻译结果</h4><div class="info-grid">';
        for (const [lang, translation] of Object.entries(queryInfo.translations)) {
            if (translation) {
                html += `
                    <div class="info-item">
                        <strong>${getLanguageName(lang)}</strong>
                        ${escapeHtml(translation)}
                    </div>
                `;
            }
        }
        html += '</div>';
    }

    // Show embedding info
    if (queryInfo.has_vector) {
        html += `
            <div style="margin-top: 15px; padding: 10px; background: #e8f5e9; border-radius: 5px;">
                 使用了语义向量搜索
            </div>
        `;
    }

    queryInfoDiv.innerHTML = html;
}

// Helper functions
function escapeHtml(text) {
    if (!text) return '';
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

function formatDate(dateStr) {
    try {
        const date = new Date(dateStr);
        return date.toLocaleDateString('zh-CN');
    } catch {
        return dateStr;
    }
}

function getLanguageName(code) {
    const names = {
        'zh': '中文',
        'en': 'English',
        'ru': 'Русский',
        'ar': 'العربية',
        'ja': '日本語',
        'unknown': '未知'
    };
    return names[code] || code;
}

// Initialize page
document.addEventListener('DOMContentLoaded', function() {
    console.log('SearchEngine Frontend loaded');
    console.log('API Base URL:', API_BASE_URL);

    // Focus on search input
    document.getElementById('searchInput').focus();
});