前端本地模糊搜索1.0 按照匹配位置加權

需求背景

公司項目為Saas ERP系統,客戶需要快速開單需要避免接口帶來的延遲問題。所以需要將商品數據保存在本地。所以本地搜索 + 權重 這一套組合拳需要前端自己實現。

搜索示例
示例1:輸入:"男士真皮錢包"進行模糊匹配優先匹配完全同→ 如 【男士真皮錢包】若無結果,展示包含 搜索內容 的商品(OR邏輯)→ 如 【商務男士真皮錢包】→ 如 【商務男士真皮錢包plus版】→ 如 【plus版商務男士真皮錢包】規則:輸入:"123"結果:123123xxxxxx123xxx123xxx

解決方案

  • 通過搜索內容對 相關字符串進行切割,以第一個切割位置為準。(例如:"testabc"用"a"分割得到[“test”, “bc”])
  • 針對 ‘test’ 和 ‘bc’ 進行加權重計算 (切割后前面字符串長度 + 1) * 1000 + 切割后后面字符串總長度

技術細節

搜索字段優先級排列
let endResultData = []
// 搜索關鍵詞轉為小寫字母
const searchLower = this.searchText.toLocaleLowerCase().trim()
// 所有支持搜索的字段,且此處也是搜索的優先級順序。
let allKeys = ['b', 'c', 'j', 'zjf', 'v', 'w', 'x']
// 儲存優先級字段
let allKeyObj = {}
// 構造存儲不匹配搜索項key的map
allKeys.map(item => {Reflect.set(allKeyObj, item, {equalArr: [],hasMapKeys: [],})
})
區分完全匹配、部分匹配、完全不匹配數據
let otherItems = []
let resultListMap = new Map()
dataList.map((item, idx) => {const filterText = item.filterText// 匹配策略if (filterText.includes(searchLower)) {let equalKeylet hasKeyallKeys.map(keyIt => {let str = String(item[keyIt]).toLocaleLowerCase()if (str === searchLower) {equalKey = keyIt} else if (str.includes(searchLower)) {hasKey = keyIt}})if (equalKey) {// 完全匹配} else {if (hasKey) {// 部分匹配} else otherItems.push(item) // 不匹配}}
})
權重計算邏輯
  1. 根據關鍵字使用split 拆分字段
  2. 根據字段拆分結果計算權重 (切割后前面字符串長度 + 1) * 1000 + 切割后后面字符串總長度
  3. 排序整合返回搜索結果數據
/*** 對指定字段進行分割,并計算匹配部分的長度信息(用于搜索結果排序或高亮處理)* @param {Object} data - 原始數據對象,需包含待處理的字段* @param {string} key - 數據對象中需要處理的字段名* @param {string} searchLower - 搜索關鍵詞(小寫格式,用于分割字符串)* @returns {Object} - 返回包含原始數據和計算長度信息的新對象*/
splitSortSearchData(data, key, searchLower) {// 使用搜索詞分割目標字符串,生成數組(例如:"testabc"用"a"分割得到["test", "bc"])const keySplitArr = data[key].split(searchLower)/*** 計算剩余部分總長度的工具函數* @param {Array} endArr - 分割后的剩余部分數組* @returns {number} - 剩余部分字符總長度*/const comptedEndLen = endArr => endArr.map(item => item.length).reduce((x, y) => x + y, 0)// 獲取分割后的剩余部分(排除第一個匹配項之前的內容)let endLenArr = keySplitArr.slice(1)let endLen = 0/* 計算剩余部分總長度邏輯:1. 當剩余部分只有1個元素時,直接取長度(需排除空字符串情況)2. 多個元素時累加各段長度3. 無剩余元素時保持默認值0*/if (endLenArr.length == 1) {// 處理單個剩余元素的情況(例如:精確匹配結尾時可能產生空字符串)if (endLenArr[0]) endLen = endLenArr[0].length// 多個剩余元素時計算總長度(例如:多次匹配產生的多段文本)} else if (endLenArr.length > 1) endLen = comptedEndLen(endLenArr)// 返回增強后的數據對象,包含:// - 原始數據的所有屬性// - startLen: 第一個匹配項前的字符長度(用于判斷匹配位置)// - endLen: 匹配項之后所有剩余字符總長度(用于相關性排序)return {...data,// 保留原始數據startLen: keySplitArr[0].length,// 首個分割段的長度(搜索詞首次出現前的字符數)endLen,// 后續所有分割段的字符總數(越小說明匹配越靠前/內容越相關)}
}/*** 拼接唯一key* @param {* Object} item 計算好前后空格的每一項* @param {* Number} idx* @returns string*/
calcOnlyKey(item, idx) {return `${(item.startLen + 1) * 1000 + item.endLen}д${idx}`
}
計算完成合并計算結果
let mapKeys = []
allKeys.forEach((item, idx) => {/* 將完全匹配的加入到最終數組中 */endResultData = endResultData.concat(allKeyObj[item].equalArr)/** 針對每一類數據進行排序*/mapKeys = mapKeys.concat(allKeyObj[item].hasMapKeys.sort((a, b) => Number(a.split('д')[0]) - Number(b.split('д')[0])))
})
mapKeys.map(mkey => endResultData.push(resultListMap.get(mkey)))
endResultData.concat(otherItems)
console.log('篩選出來數據長度:', endResultData.length)

完整代碼

// 搜索函數
onSearch(dataList) {let endResultData = []// 搜索關鍵詞轉為小寫字母const searchLower = this.searchText.toLocaleLowerCase().trim()// 所有支持搜索的字段,且此處也是搜索的優先級順序。let allKeys = ['b', 'c', 'j', 'zjf', 'v', 'w', 'x']// 儲存優先級字段let allKeyObj = {}// 構造存儲不匹配搜索項key的mapallKeys.map(item => {Reflect.set(allKeyObj, item, {equalArr: [],hasMapKeys: [],})})let otherItems = []let resultListMap = new Map()dataList.map((item, idx) => {const filterText = item.filterText// 匹配策略if (filterText.includes(searchLower)) {let equalKeylet hasKeyallKeys.map(keyIt => {let str = String(item[keyIt]).toLocaleLowerCase()if (str === searchLower) {equalKey = keyIt} else if (str.includes(searchLower)) {hasKey = keyIt}})if (equalKey) {const splitItem = this.splitSortSearchData(item, equalKey, searchLower)allKeyObj[equalKey].equalArr.push(splitItem)} else {if (hasKey) {const splitItem = this.splitSortSearchData(item, hasKey, searchLower)const key = this.calcOnlyKey(splitItem, idx)allKeyObj[hasKey].hasMapKeys.push(key)resultListMap.set(key, splitItem)} else otherItems.push(item)}}})let mapKeys = []allKeys.forEach((item, idx) => {/* 將完全匹配的加入到最終數組中 */endResultData = endResultData.concat(allKeyObj[item].equalArr)/** 針對每一類數據進行排序*/mapKeys = mapKeys.concat(allKeyObj[item].hasMapKeys.sort((a, b) => Number(a.split('д')[0]) - Number(b.split('д')[0])))})mapKeys.map(mkey => endResultData.push(resultListMap.get(mkey)))endResultData.concat(otherItems)console.log('篩選出來數據長度:', endResultData.length)return endResultData
}/**
* 對指定字段進行分割,并計算匹配部分的長度信息(用于搜索結果排序或高亮處理)
* @param {Object} data - 原始數據對象,需包含待處理的字段
* @param {string} key - 數據對象中需要處理的字段名
* @param {string} searchLower - 搜索關鍵詞(小寫格式,用于分割字符串)
* @returns {Object} - 返回包含原始數據和計算長度信息的新對象
*/
splitSortSearchData(data, key, searchLower) {// 使用搜索詞分割目標字符串,生成數組(例如:"testabc"用"a"分割得到["test", "bc"])const keySplitArr = data[key].split(searchLower)/*** 計算剩余部分總長度的工具函數* @param {Array} endArr - 分割后的剩余部分數組* @returns {number} - 剩余部分字符總長度*/const comptedEndLen = endArr => endArr.map(item => item.length).reduce((x, y) => x + y, 0)// 獲取分割后的剩余部分(排除第一個匹配項之前的內容)let endLenArr = keySplitArr.slice(1)let endLen = 0/* 計算剩余部分總長度邏輯:1. 當剩余部分只有1個元素時,直接取長度(需排除空字符串情況)2. 多個元素時累加各段長度3. 無剩余元素時保持默認值0*/if (endLenArr.length == 1) {// 處理單個剩余元素的情況(例如:精確匹配結尾時可能產生空字符串)if (endLenArr[0]) endLen = endLenArr[0].length// 多個剩余元素時計算總長度(例如:多次匹配產生的多段文本)} else if (endLenArr.length > 1) endLen = comptedEndLen(endLenArr)// 返回增強后的數據對象,包含:// - 原始數據的所有屬性// - startLen: 第一個匹配項前的字符長度(用于判斷匹配位置)// - endLen: 匹配項之后所有剩余字符總長度(用于相關性排序)return {...data,// 保留原始數據startLen: keySplitArr[0].length,// 首個分割段的長度(搜索詞首次出現前的字符數)endLen,// 后續所有分割段的字符總數(越小說明匹配越靠前/內容越相關)}
}
/**
* 拼接唯一key
* @param {* Object} item 計算好前后空格的每一項
* @param {* Number} idx
* @returns string
*/
calcOnlyKey(item, idx) {return `${(item.startLen + 1) * 1000 + item.endLen}д${idx}`
}

小結

文章最后歡迎各位大佬留言討論。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/93983.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/93983.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/93983.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux學習-網絡編程2

1.tcp可能出現粘包解決:要讓消息之間有邊界1.結束標志 \r\n2.固定長度3.協議結構體2.recv和sendrecv原型:ssize_t recv(int sockfd, void *buf, size_t len, int flags); 功能:從sockfd接收信息 參數:sockfd:要…

【普通地質學】構造運動與地質構造

名詞解釋走向:傾斜的層面與水平面的交線走向線,走向線兩端延伸的方向即為走向;構造運動:由于地球內部動力引起的組成巖石圈物質的機械運動,也可稱地殼運動或巖石圈運動;按方向分為垂直運動和水平運動&#…

基于Python的旅游推薦系統 Python+Django+Vue.js

本文項目編號 25009 ,文末自助獲取源碼 \color{red}{25009,文末自助獲取源碼} 25009,文末自助獲取源碼 目錄 一、系統介紹1.1 用戶功能描述1.2 管理員功能描述 二、系統錄屏三、啟動教程四、功能截圖五、文案資料5.1 選題背景5.2 國內外研究…

基于51單片機的智能加濕器設計 溫濕度水位防干燒手動自動聲光報警

1 系統功能介紹 本設計實現了一種 基于 51 單片機的智能加濕器控制系統。隨著現代生活水平的提高,人們對居住和辦公環境的舒適度要求越來越高,空氣濕度和溫度的調節逐漸成為家庭和辦公自動化的重要組成部分。傳統加濕器僅能實現簡單的加濕功能&#xff0…

開發避坑指南(31):Oracle 11g LISTAGG函數使用陷阱,缺失WITHIN子句解決方案

錯誤信息 Error querying database. Cause: java.sql.SQLSyntaxErrorException: ORA-02000: 缺失 WITHIN 關鍵字查詢語句 使用LISTAGG函數將多行數據合并為單行字符串,如下: selectt.order_no as orderNo,t.account_no,(select listagg(a.bank_name,,) …

【虛擬化】磁盤置備方式的性能損耗對比

【虛擬化】磁盤置備方式的性能損耗對比摘要1、定義1.1厚置備(Thick Provisioning)1.2厚置備延遲置零(Thick Provisioned Lazy Zeroed)1.3厚置備置零(Thick Provisioned Eager Zeroed)2、對比摘要 探索三種…

計算機網絡:TCP、UDP

一、TCP粘包問題(一)什么是粘包?TCP粘包是指發送方發送的多個數據包在接收方接收時被合并成一個大的數據包的現象。這種現象是由于TCP協議本身的特性導致的,TCP是面向流的協議,數據在傳輸過程中沒有明確的邊界。&#…

使用 Google 開源 AI 工具 LangExtract 進行結構化信息抽取

導讀:本文介紹科技大廠 Google 2025年 7 月最新開源的 Python 庫:LangExtract,用于從非結構文本提取結構化數據,以及非官方的 Javascript、Rust 語言實現版本。 文章目錄一、關于 LangExtract1.1 需求痛點1.2 LangExtract1.3 參考…

把 AI 變成「會說話的盲道」——基于骨傳導的地磚級語音導盲磚

標簽:城市無障礙、骨傳導、TinyML、語音導航、太陽能、離線推理、ESP32-C3、邊緣 AI ---- 1. 背景:為什么盲道要開口說話? 全國 1700 萬視障者,城市道路卻常出現: ? 盲道被違停車、廣告牌截斷; ? 傳統導…

解析三品汽車零部件PLM系統解決方案:如何助力行業解決研發管理難題

2024年,全球汽車零部件市場規模超1.5萬億美元,中國市場規模達4.6萬億元人民幣。產業繁榮高度依賴汽車產業的發展,2024年中國汽車產銷量均突破3100萬輛,新能源汽車銷量約1286萬輛,2019-2024年復合增長率達76.59%。當前行…

【RA-Eco-RA4E2-64PIN-V1.0 開發板】步進電機驅動

【RA-Eco-RA4E2-64PIN-V1.0 開發板】步進電機驅動 本文介紹了 RA-Eco-RA4E2-64PIN-V1.0 開發板驅動 28BYJ-48 步進電機的設計。 項目介紹 硬件連接:28BYJ-48 步進電機、ULN2003 驅動板、Jlink 調試器等;工程創建:GPIO 和 UART 的配置&#xf…

機器人爆發、汽車換代,速騰聚創開始講新故事

文|劉俊宏編|王一粟2025年智能汽車出貨量激增,堪稱“智駕安全帶”的激光雷達,迎來了自己的iPhone時刻。8月21日,速騰聚創發布了2025年第二季度及中期業績報告,激光雷達的中場戰事得以一并揭開。速騰聚創二季…

在Excel和WPS表格中如何隱藏單元格的公式

Excel和WPS表格中有數據、公式、圖表等以后,要發給他人查閱,如果不希望表格中的公式被查閱和修改,我們可以通過兩個步驟把公式隱藏起來。先設置有公式的單元格格式為隱藏,然后保護工作表即可。第一步:設置單元格格式為…

Eino 開源框架全景解析 - 以“大模型應用的搭積木指南”方式理解(一)

Eino 開源框架全景解析 - 大模型應用的搭積木指南 🎯 什么是 Eino?一句話概括 Eino 是字節跳動開源的大語言模型應用開發框架,就像是一個專門為 AI 應用設計的"搭積木工具箱",讓開發者能夠像搭樂高一樣輕松構建復雜的 A…

大語言模型原理(Transformer架構)

一、概覽1.1 定義大語言模型(LLM)是基于深度學習和神經網絡的自然語言處理技術,目前主要通過Transformer架構和大規模數據訓練來理解和生成語言。GPT不同架構的訓練參數:GPT-1(2018):1.17億參數GPT-2(2018)&#xff1a…

Nginx npm + Node.js 簡單實踐

一、基本概念介紹 Nginx 是一款高性能的 Web 服務器和反向代理服務器,而 Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運行環境,可以讓JavaScript 在服務器端運行。npm 則是 Node.js 的默認包管理工具,類似手機的應用市場。主要功能事故…

Python 中 SQLAlchemy 和 MySQLdb 的關系

目錄1. 角色和定位2. 工作原理和交互方式使用純 MySQLdb使用 SQLAlchemy(核心或 ORM)3. 依賴關系總結與選擇 簡單來說,它們的關系是:SQLAlchemy 是一個高層抽象的對象關系映射器(ORM)和 SQL 工具包&#xf…

【CV】OpenCV①——圖形處理簡介

一、OpenCV簡介 1. 圖像處理 1.1. 圖像起源 1.1.1. 圖像是什么1.1.2. 模擬圖像和數字圖像1.2. 數字圖像的表示 1.2.1. 位數1.2.2. 圖像分類 二值圖像灰度圖彩色圖

JAVA后端開發——API狀態字段設計規范與實踐

1. 引言在現代Web應用與API設計中,狀態(Status)字段的管理是一個普遍存在且至關重要的議題。狀態字段,如訂單狀態、任務執行狀態、模型運行狀態等,直接關系到系統的核心業務邏輯。不恰當的設計會導致API可讀性差、系統…

【MySQL的卸載】

MySQL的卸載卸載MySQL步驟1:停止MySQL服務步驟2:軟件的卸載卸載方式一:通過控制面板卸載軟件卸載方式二:通過360或電腦管家等軟件卸載卸載方式三:通過安裝包提供的卸載功能卸載步驟3:殘余文件的清理步驟4&a…