自动完成搜索框的C#LINQ Contains()查询速度很慢

时间:2019-03-06 09:06:47

标签: c# performance linq

我有一个为我提供自动完成建议的搜索框,但它的速度确实很慢,需要几秒钟的时间才能显示建议。我很确定我的代码效率低下,但是我不确定改善它的最佳方法,有什么建议吗?

[HttpPost]
    [Route("search")]
    public virtual JsonResult Search(string term)
    {
        var result = new List<SearchResult>();

        if (!String.IsNullOrWhiteSpace(term))
        {
            var searchTerms = term.ToLower().Split(' ');
            List<Card> resultList = null;
            foreach (var query in searchTerms)
            {
                if (resultList == null)
                {
                    resultList = CardRepository.FindAll().Where(x => x.Name.ToLower().Contains(query) || x.Set.SetName.ToLower().Contains(query) || x.Variant.ToLower().Contains(query) 
                    || x.CardNumber.ToLower().Contains(query) || (query == "holo" && x.IsHolo)).ToList();
                }
                else
                {
                    resultList = resultList.Where(x => x.Name.ToLower().Contains(query) || x.Set.SetName.ToLower().Contains(query) || x.Variant.ToLower().Contains(query) 
                    || x.CardNumber.ToLower().Contains(query) || (query == "holo" && x.IsHolo)).ToList();
                }
            }

            foreach (var item in resultList.Take(10))
            {
                result.Add(new SearchResult()
                {
                    label = item.FullCardName,
                    value = item.CardId.ToString()
                });
            }
        }

        return Json(result);
    }

编辑:添加了FindAll()代码。

 private readonly IDatabase _database;
 public IQueryable<Card> FindAll()
 {
     return _database.CardDataSource.OrderBy(a => a.Name).AsQueryable();
 }

解决方案:继续从评论中获得建议,并参考本文Full Text Search with LINQ,我将搜索作为一种方法移至存储库,结果几乎是即时的自动完成建议。我不确定性能可以提高多少,但是在当前状态下是否容易使用。

public Card[] Search(string[] searchTerms)
{
    IQueryable<Card> cardQuery = _database.CardDataSource;
    foreach(var term in searchTerms)
    {
        var currentTerm = term.Trim();
        cardQuery = cardQuery.Where(p => (p.Name.Contains(currentTerm) ||
                                            p.Variant.Contains(currentTerm) ||
                                            p.CardNumber.Contains(currentTerm) ||
                                            p.Set.SetName.Contains(currentTerm) ||
                                            (term == "holo" && p.IsHolo) ||
                                            (term == "reverse" && p.IsHolo))
                                        );
    }

    return cardQuery.Take(10).ToArray();
}

[HttpPost]
[Route("search")]
public virtual JsonResult Search(string term)
{
    var result = new List<SearchResult>();

    if (!String.IsNullOrWhiteSpace(term))
    {
        var searchTerms = term.ToLower().Split(' ');
        var resultList = CardRepository.Search(searchTerms);

        foreach (var item in resultList)
        {
            result.Add(new SearchResult()
            {
                label = item.FullCardName,
                value = item.CardId.ToString()
            });
        }
    }

    return Json(result);
}

2 个答案:

答案 0 :(得分:0)

我认为主要的问题是您正在使用.FindAll()并返回List<T>

这意味着当您说CardRepository.FindAll()时,它会将所有记录放入内存列表中,然后对整个列表运行随后的优化查询(例如Where(x => x.Name.ToLower().Contains(query))等) 。因此,它的返回速度确实很慢。

您可以尝试通过简单地删除.FindAll()来重写它,然后看看会发生什么。

请注意,我只是给您介绍一个主要问题,还有其他问题,但是没有一个比这个重要。

答案 1 :(得分:-4)

您可以像这样使用多线程(伪C#代码):

var allCards = CardRepository.FindAll().ToArray(); // Ensure array.
query = query.ToUpper();

var nameTask = Task.StartNew(() => allCards.Where(x => x.Name.ToUpper().Contains(query)).ToArray());
var setTask = Task.StartNew(() => allCards.Where(x => x.Set.SetName.ToUpper().Contains(query)).ToArray());
var variantTask = Task.StartNew(() => allCards.Where(x => x.Variant.ToUpper().Contains(query)).ToArray());
var cardNumberTask = Task.StartNew(() => allCards.Where(x => x.CardNumber.ToUpper().Contains(query)).ToArray());
var holoTask = Task.StartNew(() => allCards.Where(x => query == "holo" && x.IsHolo).ToArray());

Task.WaitAll(new Task[] {nameTask, setTask, variantTask, cardNumberTask, holoTask});

var result = (nameTask.Result + setTask.Result + variantTask.Result + cardNumberTask.Result + halaTask.Result).Distinct().ToArray();