欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

前端JavaScript實現大數據前后模糊搜索的方法詳解

 更新時間:2023年12月26日 09:57:06   作者:孫迅  
這篇文章主要為大家詳細介紹了前端JavaScript實現大數據前后模糊搜索的四個常見方法,文中的示例代碼講解詳細,感興趣的小伙伴可以了解下

一、背景

在特定的一些場景中,服務端可能會返回我們大量的數據,并希望在前端處理模糊查詢。我們可以參考一下方案,如果有更好的實現方案可以直接添加。

二、解決方案

1、indexedDB

目前主流前端存儲方案:

特性cookielocalStoragesessionStorageindexedDB
數據生命周期一般由服務器生成,可以設置過期時間;前端采用和js-cookie等組件也可以生成除非被清理,否則一直存在;瀏覽器關閉還會保存在本地,但是不支持跨瀏覽器頁面關閉就清理刷新依然存在,不支持跨頁面交互限制為可用磁盤空間的 50%。
數據存儲大小4K5M5M不限制大小
與服務端通信每次都會攜帶在請求的header 中,對于請求性能有影響;同時由于請求中都帶有,所以也容易出現安全問題不參與不參與不參與
特點字符串鍵值對在本地存儲數據字符串鍵值對在本地存儲數據字符串鍵值對在本地存儲數據可以存儲大量數據,提供接口來查詢,還可以建立索引

IndexedDB是個非關系型數據庫,那我們也可以去進行查詢搜索。對于搜索速度方面可能不是最優(yōu)解,但是要考慮業(yè)務實現,例如:我們有一個接口,接口數據更新不是那么頻繁,并且數據量非常大,調用接口時間可能會很久,搜索某一項或者某個開始的可能會更好點。

function init() {
  // 打開 IndexedDB 數據庫
  const openDB = window.indexedDB.open("myDatabase", 1);
  // 新建數據庫和數據庫版本號發(fā)生變化時才被調用
  openDB.onupgradeneeded = function (event) {
    const db = event.target.result;
    // 創(chuàng)建對象存儲空間
    const objectStore = db.createObjectStore("myObjectStore", {
      keyPath: "code",
    });
    // 添加索引
    // objectStore.createIndex("code", "code");
    objectStore.createIndex("pCode", "pCode", { unique: false });
    objectStore.createIndex("name", "name", { unique: false });
  };

  openDB.onsuccess = async function (event) {
    const db = event.target.result;
    const dataArray = data;
    // 獲取事務對象
    const transaction = db.transaction(["myObjectStore"], "readwrite");
    // 獲取對象存儲空間
    const objectStore = transaction.objectStore("myObjectStore");

    // 向對象存儲添加對象
    dataArray.forEach(function (data) {
      const addRequest = objectStore.add(data);

      addRequest.onsuccess = function () {
        console.log("數據已成功添加到對象存儲");
      };

      addRequest.onerror = function (error) {
        console.error("添加數據時發(fā)生錯誤", error);
      };
    });
  };
}
function search() {
  window.result = [];
  // 打開數據庫
  const openRequest = window.indexedDB.open("myDatabase", 1);
  // 打開數據庫成功回調
  openRequest.onsuccess = function (event) {
    // 數據庫對象
    const db = event.target.result;
    // 打開只讀事務并獲取對象存儲 readwrite 讀寫  readonly 只讀
    const transaction = db.transaction(["myObjectStore"], "readonly");
    // 獲取myObjectStore對象存儲空間的引用
    const objectStore = transaction.objectStore("myObjectStore");
    // 獲取 name 索引
    const index = objectStore.index("name");
    // 打開游標
    const request = index.openCursor(); // 全部數據
    // const request = index.openCursor(searchKey); // 精準匹配
    // const request = index.openCursor(
    //   IDBKeyRange.bound(searchKey, searchKey + "\uffff", false, false)
    // ); // 后模糊
    const startTime = new Date().getTime();
    request.onsuccess = function (event) {
      const cursor = event.target.result;
      if (cursor) {
        const name = cursor.value.name;
        // 判斷 name 是否包含搜索關鍵字 如果是后模糊的話 直接拿name就行
        if (regex.test(name)) {
          result.push(cursor.value);
        }
        cursor.continue();
      } else {
        const endTime = new Date().getTime();
        console.log(`搜索耗時:${endTime - startTime}`);
        console.log(result);
      }
    };

    request.onerror = function () {
      console.error("執(zhí)行搜索時發(fā)生錯誤");
    };
  };
  // 打開數據庫失敗回調
  openRequest.onerror = function () {
    console.error("打開數據庫時發(fā)生錯誤");
  };
}

2、Trie tree(字典樹)

其原理:就是將所有的字都拆分,并將后面的字全部拆分加入自己的孩子節(jié)點里面。其實更加契合后模糊匹配,如果是需要前后模糊匹配,需要全部遍歷,性能也不是最佳。

class TrieNode {
  constructor() {
    this.children = new Map(); // 用于存儲當前節(jié)點的子節(jié)點
    this.isEndOfWord = false; // 表示當前節(jié)點是否是一個單詞的結尾
    this.values = []; // 用于存儲與當前節(jié)點關聯的值
  }
}

class Trie {
  constructor() {
    this.root = new TrieNode();
  }
  // 如果當前字符不存在于當前節(jié)點的子節(jié)點中,則創(chuàng)建一個新的子節(jié)點,
  // 并將其添加到子節(jié)點Map中。然后,將當前節(jié)點更新為新的子節(jié)點,繼續(xù)遍歷下一個字符。
  // 當遍歷完所有字符后,將當前節(jié)點的isEndOfWord屬性設置為true,表示該節(jié)點是一個單詞的結尾。
  // 同時,將與該單詞關聯的值添加到當前節(jié)點的values數組中。
  insert(word, value) {
    let node = this.root;
    for (let i = 0; i < word.length; i++) {
      const char = word[i];

      if (!node.children.has(char)) {
        node.children.set(char, new TrieNode());
      }
      node = node.children.get(char);
    }
    node.isEndOfWord = true;
    node.values.push(value);
  }

  // 遞歸地收集以給定節(jié)點為根的子樹中所有單詞的關聯值。如果當前節(jié)點是一個單詞的結尾
  // 則將該節(jié)點的所有關聯值添加到結果集合中。然后,對當前節(jié)點的每個子節(jié)點
  // 遞歸調用collectValues方法。
  collectValues(node, results) {
    if (node.isEndOfWord) {
      for (const value of node.values) {
        results.add(value);
      }
    }
    for (const childNode of node.children.values()) {
      this.collectValues(childNode, results);
    }
  }

  // 模糊搜索與給定關鍵字匹配的單詞,并返回匹配到的所有關聯值。該方法首先創(chuàng)建一個空的
  // Set對象results,用于存儲結果。然后,調用collectSuffixValues方法,
  // 從根節(jié)點開始遞歸地搜索與關鍵字匹配的單詞,并將匹配到的關聯值添加到results中。
  // 最后,將results轉換為數組并返回。
  fuzzySearch(keyword) {
    const results = new Set();
    this.collectSuffixValues(this.root, keyword, results);
    return Array.from(results);
  }

  // 遞歸地搜索與給定關鍵字后綴匹配的單詞,并將匹配到的關聯值添加到結果集合中。
  // 如果關鍵字的長度為0,表示已經匹配到了關鍵字的末尾,此時調用collectValues方法,
  // 將當前節(jié)點及其子樹中的所有關聯值添加到結果集合中。
  // 否則,取關鍵字的第一個字符char,并獲取當前節(jié)點的子節(jié)點childNode。
  // 如果childNode存在,則遞歸調用collectSuffixValues方法,
  // 將關鍵字的剩余部分和childNode作為參數傳遞。
  // 然后,對當前節(jié)點的每個子節(jié)點,遞歸調用collectSuffixValues方法,
  // 將關鍵字和結果集合作為參數傳遞
  collectSuffixValues(node, keyword, results) {
    if (keyword.length === 0) {
      this.collectValues(node, results);
    } else {
      const char = keyword[0];
      const childNode = node.children.get(char);
      if (childNode) {
        this.collectSuffixValues(childNode, keyword.slice(1), results);
      }
    }
    for (const childNode of node.children.values()) {
      this.collectSuffixValues(childNode, keyword, results);
    }
  }
}

async function search() {
  // 示例用法
  const trie = new Trie();
  const dataArray = await getJSON();
  dataArray.forEach((item) => {
    trie.insert(item.name, item);
  });
  const startTime = new Date().getTime();
  const results = trie.fuzzySearch("總部");
  const endTime = new Date().getTime();
  console.log(`搜索耗時:${endTime - startTime}`);
  console.log(trie, "results", results);
}
search();

3、Fuse(第三方組件庫)

Bitap算法是fuse.js中用于實現模糊搜索的核心算法之一,其主要思路是利用位運算來計算模式串和目標串之間的相似度。具體來說,Bitap算法首先將模式串轉換為二進制掩碼,并利用位運算來計算模式串和目標串之間的相似度,然后采用一些啟發(fā)式策略來提高算法的準確性和效率。其他講解

function search() {
  const startTime = new Date().getTime();
  const fuseOptions = {
    // isCaseSensitive: false, // 指示比較是否應區(qū)分大小寫。
    // includeScore: true, // 分數是否應包含在結果集中。分數0表示完全匹配,分數1表示完全不匹配。
    // shouldSort: true, // 是否按分數對結果列表進行排序。
    // includeMatches: true, // 匹配是否應包含在結果集中。當 時true,結果集中的每條記錄都將包含匹配字符的索引。因此,這些可以用于突出顯示的目的。
    // findAllMatches: true, // 當為 true 時,匹配函數將繼續(xù)到搜索模式的末尾,即使已在字符串中找到完美匹配。
    // minMatchCharLength: 1, // 僅返回長度超過該值的匹配項。(例如,如果您想忽略結果中的單個字符匹配,請將其設置為2)。
    // location: 0, // 確定預期在文本中找到的模式的大概位置。
    // threshold: 0.6, // 匹配算法在什么時候放棄。閾值0.0需要完美匹配(字母和位置),閾值1.0可以匹配任何內容
    // distance: 100, // 確定匹配與模糊位置的接近程度(由 指定location)。distance遠離模糊位置的字符的精確字母匹配將被評分為完全不匹配。A distanceof0要求匹配精確指定location。距離1000需要在使用of找到的800字符內有完美匹配。locationthreshold0.8
    // ignoreLocation: true, // true,搜索將忽略location和distance,因此模式出現在字符串中的位置并不重要。
    // ignoreFieldNorm: true, //true,相關性得分(用于排序)的計算將忽略字段長度范數。
    // fieldNormWeight: 1, // 確定字段長度范數對評分的影響程度。值0相當于忽略字段長度范數。值0.5將大大降低字段長度范數的影響,而值2.0將大大增加字段長度范數的影響。
    keys: ["name"],
  };
  const fuse = new Fuse(data, fuseOptions);
  const searchPattern = "肉食總部人力資源部";
  result = fuse.search(searchPattern);
  const endTime = new Date().getTime();
  console.log(`搜索耗時:${endTime - startTime}`);
  console.log(result);
}
配置項描述默認值說明
keys要搜索的鍵的列表。它支持嵌套路徑、加權搜索、在字符串和對象數組中搜索。
isCaseSensitive是否區(qū)分大小寫false
includeScore分數是否應包含在結果集中false分數0表示完全匹配,分數1表示完全不匹配。
shouldSort是否按分數對結果列表進行排序。true
includeMatches匹配是否應包含在結果集中。
findAllMatches匹配是否應全部在結果集中。false當為 true 時,匹配函數將繼續(xù)到搜索模式的末尾,即使已在字符串中找到完美匹配。
minMatchCharLength僅返回長度超過該值的匹配項。1如果您想忽略結果中的單個字符匹配,請將其設置為2
location確定預期在文本中找到的模式的大概位置。0
threshold匹配算法在什么時候放棄0.6閾值0.0需要完美匹配(字母和位置),閾值1.0可以匹配任何內容
distance確定匹配與模糊位置的接近程度(由 指定location)100distance遠離模糊位置的字符的精確字母匹配將被評分為完全不匹配。A distanceof0要求匹配精確指定location。距離1000需要在使用of找到的800字符內有完美匹配。locationthreshold0.8
ignoreLocation搜索將忽略location和distancefalse
ignoreFieldNorm相關性得分(用于排序)的計算將忽略字段長度范數false
fieldNormWeight確定字段長度范數對評分的影響程度。1值0相當于忽略字段長度范數。值0.5將大大降低字段長度范數的影響,而值2.0將大大增加字段長度范數的影響。

4、前綴(Trie)樹+交集實現模糊搜索

之前我們提過的第二種方案更適合前綴查詢,但是我們通過循環(huán)整個樹的話,是可以實現前后模糊查詢,但不是最佳方案,如果是前后模糊查詢,更加推薦以下這種方案。

我們之前是將數組種的文字第一個進行一個拆解。其實我們可以轉換一種思路,通過將所有的字都拆解,然后每個樹結點中有一個字段儲存所有包含這個字的唯一索引。

let array = [{id:1,name:"總部"},{id:2,name:"總"},{id:3,name:"部"}]

let result = {"總":{ids:[1,2]},"部":{ids:[1,3]}}

當我們輸入“總”字時,結果應該為1,2;

輸入部時,結果應該為1,3;

輸入總部時,結果應該為1;

其實在輸入總部的時候也就是取兩個字的交集,他們共同含有的1

如果搜索的是多個字的話也是同樣的道理,取每個字的索引,找出它們的交集,也就是他們的搜索結果,同時也可以實現,總*部、總部**等,這種包含的情況。

以上就是前端JavaScript實現大數據前后模糊搜索的方法詳解的詳細內容,更多關于JavaScript模糊搜索的資料請關注腳本之家其它相關文章!

相關文章

最新評論