首页 > 解决方案 > 如何在打字时为实时搜索创建缓存系统

问题描述

我试图在键上创建一个搜索引擎以从数据库返回结果,但如果已键入短语以从对象而不是从数据库中提取结果

例如:当您输入“BMW 300”时,将从数据库中提取数据并将短语和结果保存在对象中,对象将如下所示

object {
b: (4) […]
  0: Object { name: "BMW"}
  1: Object { name: "BMW 300"}
  2: Object { name: "BMW XS"}
  3: Object { name: "BMW Z 500"}

bm: (4) […]
  0: Object { name: "BMW"}
  1: Object { name: "BMW 300"}
  2: Object { name: "BMW XS"}
  3: Object { name: "BMW Z 500"}

bmw: (4) […]
  0: Object { name: "BMW"}
  1: Object { name: "BMW 300"}
  2: Object { name: "BMW XS"}
  3: Object { name: "BMW Z 500"}

"bmw 3": (1) […]
​  0: Object { name: "BMW 300"}

"bmw 30": (1) […]
​  0: Object { name: "BMW 300"}

"bmw 300": (1) […]
​  0: Object { name: "BMW 300"}
}

如果您删除 300 个短语变成“BMW”,现在应该从对象中提取结果,因为短语“BMW”已被输入。

我对两个逻辑都进行了编码,它们工作得很好,但问题是如何交换何时从对象读取以及何时从数据库读取?

// create object
var hold_results = {};

$('input[name="search"]').on('keyup', function(){

var phrase = jQuery.trim($(this).val());

if (phrase.length > 0) {
  $.ajax({
    type: "POST",
    url: 'index.php?route=product/newsearch',
    data: 'phrase='+phrase,
    success: function(data) {
      if (data.length != 0) {

      // push phrase and currect results in object
        hold_results[phrase] = data;


          $.each(data , function(key, value){
            if (value != '') {
              html = '<li><a href="'+value.href+'">'+value.name+'</a></li>';
              $('#search-results').append(html);
            }
        });
      }
    }
  });
}

// should pull results from object here if phrase exist
$.each(hold_results , function(key , value){

  if (key == phrase) {
    $.each(value, function(k , v){
      html = '<li><a href="'+v.href+'">'+v.name+'</a></li>';
      $('#search-results').append(html);
    })
  }
});
});

标签: jqueryajax

解决方案


有一种简单的方法可以实现这一点 - 但首先您需要知道您的实现会触发太多次并且可能无法按预期工作。我会考虑添加几件事:

  1. 在触发新搜索之前中止先前触发的异步 ajax 请求 - 在您的代码中,它将继续触发并接收许多可能不再相关的结果。
  2. 添加缓存机制- 您想要控制它将存储多少记录,并且您想要一种匹配所需搜索词的好方法(精确匹配或子集或两者)。

这是 JSfiddle 中的演示- 我将引导您完成

首先,我们创建一个变量来保存一个活动的 ajax 请求,否则将是null

//this is the variable that will store the ajax call
var request = null;

现在我们可以使用所有必需的方法创建一个对象,它将成为我们的缓存系统:

var cache = {
  storageFIFO: [], // The array that will hold our results 
  maxResults: 20, // max enteries in the storedResultFifo array - will shift once when exceeded
  pushResults: function(phrase, data) {
    cache.storageFIFO.push({
      term: phrase,
      data: data
    });
    if (cache.storageFIFO.length > cache.maxResults) {
      cache.storageFIFO.shift();
    }
  },
  getCachedResults: function(phrase) {
    //First try exact match against cached search terms:
    for (var i = 0; i < cache.storageFIFO.length; i++)
      if (cache.storageFIFO[i].term === phrase)
        return cache.storageFIFO[i].data;
    //try phrase as a substring of the terms stored in the cache
    for (var i = 0; i < cache.storageFIFO.length; i++)
      if (cache.storageFIFO[i].term.includes(phrase))
        return cache.storageFIFO[i].data;
    return false;
  }
};

关于缓存的几点说明:

  1. FIFO(先进先出):当达到存储限制时,删除最旧的记录。
  2. .shift();将弹出第一个元素(最旧的索引 0)以实现类似 FIFO 的数据结构。
  3. getCachedResults:此方法将尝试匹配短语两次 - 一次用于完全匹配(以获得更好的结果),否则将尝试将短语匹配为缓存术语的子集。
  4. string1.includes(string2)这是我匹配子集的方式。

现在设置了缓存机制 - 我将逻辑放入keyup事件中:

//Bind the event to search:
$('input[name="search"]').on('keyup', function() {
  var phrase = jQuery.trim($(this).val());
  if (phrase.length <= 1) return; // From length of two
  //This is just for testing and generating random data:
  var randomData = [];
  for (var i = 0; i < Math.floor(Math.random() * (20 - 5 + 1)) + 5; i++)
    randomData.push({
      phrase: phrase,
      href: "#",
      name: Math.random().toString(36).substring(7)
    });
  //This will trigger an abort of previous unfinished ajax request
  //When aborted the ajax `complete` will set it back to null
  if (request != null) request.abort();

  //try to load from Cache first:
  var fromCache = cache.getCachedResults(phrase);
  if (fromCache !== false) {
    drawResults(fromCache, "Cached Results");
    return;
  }

  //If we got here that means that the cache does not has matching phrases:
  //Send a request to db:
  request = $.ajax({
    url: '/echo/json/', // A jsfiddle async request endpoint
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    data: { //the correct data structure for jsfiddle async requests
      json: JSON.stringify(randomData),
      delay: 0.15
    },
    success: function(data) {
      if (data.length != 0) {
        //Cache it:
        cache.pushResults(phrase, data);
        //Draw it: 
        drawResults(data, "DataBase");
      }
    },
    complete: function() {
      request = null;
    }
  });

});

在这里,您可以看到一些为创建的 jsfiddle 的附加内容(从虚拟异步请求服务器获取结果)。

关于 Logic 和 Ajax 请求的几点说明:

  1. if (phrase.length <= 1) return;: 将阻止不是 2 个字符或更长的查询。
  2. if (request != null) request.abort();: 将中止先前运行的请求。正如您在 Ajaxcomplete回调中看到的那样,它将在完成时将其设置回null(成功、中止、错误等......)
  3. cache.getCachedResults(phrase);:首先从缓存中尝试 -false如果不匹配,将返回。
  4. 在没有命中缓存后 - 然后触发 ajax(请注意,url 和数据被修改为所需的值以与 jsfiddle 一起使用)。

最终说明 绘制结果由您决定——我只是包含了一个小的快速函数来可视化结果并查看它们的来源。


推荐阅读