欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

在这里插入图片描述

📌 模块概述

全文搜索模块是MovieTracker应用中用于快速查找影片的功能。用户可以输入影片标题、导演、演员等关键词,系统会实时搜索并显示匹配的影片。全文搜索提供了最快速的影片查找方式,支持模糊匹配和多字段搜索。

该模块的主要功能包括:实时搜索、搜索历史、热门搜索词、搜索结果高亮、搜索统计等。通过Cordova框架与OpenHarmony原生能力的结合,实现了高效的全文搜索和搜索优化。

全文搜索需要处理大量的搜索请求,因此需要优化搜索性能。可以使用防抖技术延迟搜索请求,同时在数据库中建立索引以加快查询速度。

🔗 完整流程

第一步:搜索输入与防抖处理

当用户在搜索框中输入内容时,系统需要实时搜索。为了避免频繁的数据库查询,需要使用防抖技术延迟搜索请求。当用户停止输入一段时间后,才执行搜索操作。

搜索输入需要处理特殊字符,防止SQL注入。同时需要记录搜索历史,方便用户快速访问之前的搜索。

第二步:搜索执行与结果处理

搜索执行时需要在多个字段中进行搜索,如影片标题、导演、演员、描述等。搜索结果需要按相关性排序,最相关的结果显示在前面。同时需要处理搜索结果为空的情况,提示用户没有找到匹配的影片。

搜索结果需要进行高亮显示,将匹配的关键词标记出来,帮助用户快速识别匹配内容。

第三步:搜索优化与统计

搜索模块需要记录搜索统计信息,如搜索词频率、搜索结果数量等。这些统计信息可以用于优化搜索算法和提供热门搜索词建议。

同时需要优化搜索性能,使用缓存机制存储热门搜索词的结果,避免重复搜索。

🔧 Web代码实现

全文搜索HTML结构

<div id="search-page" class="page">
    <div class="search-container">
        <input type="text" id="search-input" placeholder="搜索影片标题、导演、演员..." 
               class="search-box" onkeyup="handleSearchInput(event)">
        <button class="btn btn-primary" onclick="clearSearch()">清空</button>
    </div>
    
    <div class="search-suggestions" id="suggestions-container" style="display: none;"></div>
    
    <div class="search-results" id="search-results"></div>
    
    <div class="search-history">
        <h3>搜索历史</h3>
        <div id="history-list" class="history-list"></div>
    </div>
</div>

这个HTML结构定义了全文搜索页面的布局。搜索框用于输入搜索关键词。suggestions-container显示搜索建议。search-results显示搜索结果。history-list显示搜索历史。

搜索实现

let searchTimeout;
const searchCache = {};

function handleSearchInput(event) {
    const keyword = event.target.value.trim();
    
    clearTimeout(searchTimeout);
    
    if (keyword.length === 0) {
        document.getElementById('search-results').innerHTML = '';
        document.getElementById('suggestions-container').style.display = 'none';
        return;
    }
    
    // 防抖:延迟300ms执行搜索
    searchTimeout = setTimeout(() => {
        performSearch(keyword);
    }, 300);
}

async function performSearch(keyword) {
    try {
        // 检查缓存
        if (searchCache[keyword]) {
            displaySearchResults(searchCache[keyword], keyword);
            return;
        }
        
        const allMovies = await db.getAllMovies();
        
        // 搜索匹配的影片
        const results = allMovies.filter(movie => {
            const searchText = `${movie.title} ${movie.director} ${movie.actors || ''} ${movie.description || ''}`.toLowerCase();
            return searchText.includes(keyword.toLowerCase());
        });
        
        // 按相关性排序
        results.sort((a, b) => {
            const aTitle = a.title.toLowerCase().includes(keyword.toLowerCase()) ? 0 : 1;
            const bTitle = b.title.toLowerCase().includes(keyword.toLowerCase()) ? 0 : 1;
            return aTitle - bTitle;
        });
        
        // 缓存结果
        searchCache[keyword] = results;
        
        // 记录搜索历史
        saveSearchHistory(keyword);
        
        displaySearchResults(results, keyword);
    } catch (error) {
        console.error('搜索失败:', error);
        showError('搜索失败');
    }
}

function displaySearchResults(results, keyword) {
    const container = document.getElementById('search-results');
    container.innerHTML = '';
    
    if (results.length === 0) {
        container.innerHTML = `<p class="empty-message">未找到包含"${keyword}"的影片</p>`;
        return;
    }
    
    const resultsList = document.createElement('div');
    resultsList.className = 'results-list';
    
    results.forEach(movie => {
        const resultItem = document.createElement('div');
        resultItem.className = 'result-item';
        
        // 高亮关键词
        const highlightedTitle = highlightKeyword(movie.title, keyword);
        
        resultItem.innerHTML = `
            <div class="result-title">${highlightedTitle}</div>
            <div class="result-meta">
                <span>${movie.year}</span>
                <span>${movie.director}</span>
                <span>⭐ ${movie.rating || '未评分'}</span>
            </div>
            <div class="result-actions">
                <button onclick="app.navigateTo('movie-detail', ${movie.id})" class="btn btn-small">查看详情</button>
            </div>
        `;
        
        resultsList.appendChild(resultItem);
    });
    
    container.appendChild(resultsList);
}

function highlightKeyword(text, keyword) {
    const regex = new RegExp(`(${keyword})`, 'gi');
    return text.replace(regex, '<mark>$1</mark>');
}

这个函数实现了全文搜索功能。handleSearchInput()处理搜索输入,使用防抖技术延迟搜索。performSearch()执行实际搜索,支持多字段搜索和缓存。displaySearchResults()显示搜索结果并高亮关键词。

搜索历史管理

async function saveSearchHistory(keyword) {
    try {
        const history = await db.getSearchHistory();
        
        // 移除重复的搜索词
        const filtered = history.filter(h => h.keyword !== keyword);
        
        // 添加新的搜索词到开头
        filtered.unshift({
            keyword: keyword,
            timestamp: Date.now()
        });
        
        // 只保留最近的20条搜索历史
        const limited = filtered.slice(0, 20);
        
        await db.saveSearchHistory(limited);
        loadSearchHistory();
    } catch (error) {
        console.error('保存搜索历史失败:', error);
    }
}

async function loadSearchHistory() {
    try {
        const history = await db.getSearchHistory();
        const container = document.getElementById('history-list');
        container.innerHTML = '';
        
        if (history.length === 0) {
            container.innerHTML = '<p class="empty-message">暂无搜索历史</p>';
            return;
        }
        
        history.slice(0, 10).forEach(item => {
            const historyItem = document.createElement('div');
            historyItem.className = 'history-item';
            historyItem.innerHTML = `
                <span onclick="searchByHistory('${item.keyword}')" class="history-keyword">${item.keyword}</span>
                <button onclick="deleteHistoryItem('${item.keyword}')" class="btn btn-small btn-danger">删除</button>
            `;
            container.appendChild(historyItem);
        });
    } catch (error) {
        console.error('加载搜索历史失败:', error);
    }
}

function searchByHistory(keyword) {
    document.getElementById('search-input').value = keyword;
    performSearch(keyword);
}

这个函数实现了搜索历史的管理。saveSearchHistory()保存搜索词到历史记录,loadSearchHistory()加载并显示搜索历史。

🔌 OpenHarmony原生代码

全文搜索插件

// SearchPlugin.ets
import { webview } from '@kit.ArkWeb';
import { common } from '@kit.AbilityKit';

export class SearchPlugin {
    private context: common.UIAbilityContext;
    
    constructor(context: common.UIAbilityContext) {
        this.context = context;
    }
    
    public registerSearch(controller: webview.WebviewController): void {
        controller.registerJavaScriptProxy({
            object: new SearchBridge(),
            name: 'searchNative',
            methodList: ['getSearchSuggestions', 'getHotSearches']
        });
    }
}

这个OpenHarmony原生插件为全文搜索提供了搜索建议和热门搜索功能。

搜索建议实现

export class SearchBridge {
    public getSearchSuggestions(keyword: string, historyJson: string): string {
        try {
            const history = JSON.parse(historyJson);
            
            // 从历史记录中获取相关建议
            const suggestions = history
                .filter(h => h.keyword.includes(keyword))
                .map(h => h.keyword)
                .slice(0, 5);
            
            return JSON.stringify({
                suggestions: suggestions,
                count: suggestions.length
            });
        } catch (error) {
            return JSON.stringify({
                error: error.message
            });
        }
    }
    
    public getHotSearches(historyJson: string): string {
        try {
            const history = JSON.parse(historyJson);
            
            // 统计搜索频率
            const frequency: any = {};
            history.forEach((h: any) => {
                frequency[h.keyword] = (frequency[h.keyword] || 0) + 1;
            });
            
            // 按频率排序
            const hotSearches = Object.entries(frequency)
                .sort((a: any, b: any) => b[1] - a[1])
                .slice(0, 10)
                .map(([keyword]) => keyword);
            
            return JSON.stringify({
                hotSearches: hotSearches,
                count: hotSearches.length
            });
        } catch (error) {
            return JSON.stringify({
                error: error.message
            });
        }
    }
}

这个类实现了搜索建议和热门搜索功能。getSearchSuggestions()基于搜索历史提供搜索建议,getHotSearches()返回最热门的搜索词。

Web-Native通信

调用原生搜索功能

async function getSearchSuggestions(keyword) {
    try {
        const history = await db.getSearchHistory();
        
        if (window.searchNative) {
            const suggestResult = window.searchNative.getSearchSuggestions(
                keyword,
                JSON.stringify(history)
            );
            const result = JSON.parse(suggestResult);
            
            displaySuggestions(result.suggestions);
        }
    } catch (error) {
        console.error('获取搜索建议失败:', error);
    }
}

async function displayHotSearches() {
    try {
        const history = await db.getSearchHistory();
        
        if (window.searchNative) {
            const hotResult = window.searchNative.getHotSearches(JSON.stringify(history));
            const result = JSON.parse(hotResult);
            
            console.log('热门搜索:', result.hotSearches);
        }
    } catch (error) {
        console.error('获取热门搜索失败:', error);
    }
}

这个函数展示了如何调用OpenHarmony原生的搜索建议功能。在用户输入时获取搜索建议,帮助用户快速找到想要的影片。

📝 总结

全文搜索模块展示了Cordova与OpenHarmony混合开发中的高效搜索和智能建议功能。通过Web层提供用户友好的搜索界面,同时利用OpenHarmony原生能力进行搜索优化和热门搜索分析。

在实现这个模块时,需要注意搜索性能的优化、搜索结果的相关性排序、以及搜索建议的准确性。通过合理的架构设计和优化策略,可以构建出高效、易用的全文搜索功能。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐