搜索大量对象

时间:2018-10-31 14:49:50

标签: javascript typescript search

我有大量要搜索的对象。

该数组包含> 60.000个项目,并且搜索性能有时会非常缓慢。

该数组中的一个对象如下所示:

{
  "title": "title"
  "company": "abc company"
  "rating": 13 // internal rating based on comments and interaction
  ...
}

我要搜索标题和公司信息,然后按商品的等级进行排序。

这是我目前的搜索结果:

    onSearchInput(searchTerm) {
        (<any>window).clearTimeout(this.searchInputTimeout);
        this.searchInputTimeout = window.setTimeout(() => {
            this.searchForFood(searchTerm);
        }, 500);
    }

    searchForFood(searchTerm) {
        if (searchTerm.length > 1) {
            this.searchResults = [];
            this.foodList.map(item => {
                searchTerm.split(' ').map(searchTermPart => {
                    if (item.title.toLowerCase().includes(searchTermPart.toLowerCase())
                        || item.company.toLowerCase().includes(searchTermPart.toLowerCase())) {
                        this.searchResults.push(item);
                    }
                });
            });

            this.searchResults = this.searchResults.sort(function(a, b) {
                return a.rating - b.rating;
            }).reverse();

        } else {
            this.searchResults = [];
        }
    }

问题:有什么方法可以改善搜索逻辑和性能?

2 个答案:

答案 0 :(得分:2)

一堆提示:

  • 在前端搜索60,000个项目有点多余。您可以通过任何方式在后端执行部分搜索吗?如果您真的必须在前端执行此操作,请考虑搜索例如10,000,然后使用setImmediate()执行搜索的下一部分,这样用户的浏览器就不会在处理期间完全冻结。
  • 在循环外进行搜索词的拆分和小写。
  • map()就像您正在使用它一样,因为您不使用返回值,这很奇怪。最好使用forEach()。更好的是,使用filter()获取匹配的项目。
  • 遍历搜索词时,请使用some()(如注释中指出的那样),因为这是提前返回的机会。
  • sort()对原始数组进行了变异,因此您无需重新分配它。
  • 带有sort()
  • reverse()通常是一种气味。相反,将条件的两边换成b - a
  • 在这种规模下,使用includes()indexOf(),roll-your-own-for-循环,match()(确保速度会变慢)

答案 1 :(得分:0)

Alex的建议很好。我唯一的建议是,如果您有能力在空闲时间进行数据预处理(最好不要先进行渲染或交互),则可以将数据处理为修改的前缀特里。这样一来,您可以在O(k)时间中搜索项目,其中k是搜索项的长度(现在,您正在O(kn)时间中进行搜索,因为您查看了每个项目,然后执行includes花费k时间(实际上由于toLowerCase而变差,但我不想涉足其中)。

如果您不熟悉Trie是什么,希望下面的代码可以为您提供帮助,或者您可以使用所选的搜索引擎搜索信息。它基本上是嵌套哈希图中字符串中字符的映射。

下面是一些有关如何构造特里的示例代码:

function makeTries(data){
    let companyTrie = {};
    let titleTrie = {};
    data.forEach(item => {
        addToTrie(companyTrie, item.company, item, 0);
        addToTrie(titleTrie, item.title, item, 0);
    });
    return {
        companyTrie,
        titleTrie
    }
}

function addToTrie(trie, str, item, i){
    trie.data = trie.data || [];
    trie.data.push(item);

    if(i >= str.length)
        return;
    if(! trie[str[i]]){
        trie[str[i]] = {};
    }
    
    addToTrie(trie[str[i]], str, item, ++i);
}

function searchTrie(trie, term){
    if(trie == undefined)
        return [];
    if(term == "")
        return trie.data;
    return searchTrie(trie[term[0]], term.substring(1));
}

var testData = [
    {
        company: "abc",
        title: "def",
        rank: 5
    },{
        company: "abd",
        title: "deg",
        rank: 5
    },{
        company: "afg",
        title: "efg",
        rank: 5
    },{
        company: "afgh",
        title: "efh",
        rank: 5
    },
];

const tries = makeTries(testData);
console.log(searchTrie(tries.companyTrie, "afg"));