【问题标题】:How to create a cache system for a live search while typing如何在打字时为实时搜索创建缓存系统
【发布时间】:2020-02-12 21:31:16
【问题描述】:

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

例如:当您输入“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);
    })
  }
});
});

【问题讨论】:

  • 如果您想使用缓存结果,请使用 GET 请求,这是从服务器或数据库读取信息的标准方法。此外,GET 请求支持缓存,但 POST 不支持。作为参考阅读这个答案stackoverflow.com/a/17104536/6009502

标签: jquery ajax


【解决方案1】:

您可以简单地检查 phrase 是否已经存在于 holds_results 对象中,以及它是否没有从 db 中获取它

  // create object
  var hold_results = {};

  $('input[name="search"]').on('keyup', function(){
    var phrase = jQuery.trim($(this).val());

    if (phrase.length > 0) {
      if (hold_results[phrase]) {
        // 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);
            })
          }
        });
      }
      else {
        $.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);
                }
              });
            }
          }
        });
      }
    }
  });

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

    • 我喜欢你的逻辑,我会在第二天回家时尝试一下,然后告诉你结果
    猜你喜欢
    • 1970-01-01
    • 2019-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-04
    相关资源
    最近更新 更多