首先是实际状态:有一个带有表单的ZF2应用程序。该表单包含一些自动完成字段(在前端使用jQuery自动完成)。
后面的SQL语句是这样的:
SELECT name FROM countries WHERE countries.name LIKE %en%
-> should find "Arg[en]tina", "Arm[en]ia", "B[en]in", "Turkm[en]istan" etc.
or
SELECT name FROM countries WHERE countries.continent_id = 2
-> should find "Afghanistan", "Armenia", "Azerbaijan" etc. (2 = Asia)
or
SELECT name FROM countries WHERE countries.continent_id = 2 AND countries.name LIKE %en%
-> should find "Arm[en]ia", "Turkm[en]istan" etc. (2 = Asia)
当然,这会导致问题,数据库被大量小的自动完成请求吓坏了。缓存应该有所帮助——我已经开始实现基于Zend'Cache'Storage'Adapter'Apcu
的缓存机制。但随后我看到了下一个问题:使用APCu这样的通用缓存,我无法动态过滤结果。因此,这样的缓存似乎不适用于自动完成的情况。
我很确定,这是一个普遍的问题,并且已经有解决方案了。
如何在ZF2应用程序中实现自动完成功能的缓存机制?
这里与ZF2无关。这都是关于自定义搜索服务的设计和它的挑战。
如果没有适当的缓存层和/或全文搜索引擎,构建这样的自动完成实现对应用程序来说无异于自杀。您可以在很短的时间内轻松地实现数万个不必要的重复查询。
在一个"理想"的世界里,一个好的自动补全实现利用一个全文搜索引擎,比如Elasticsearch或Apache Solr。并分别使用它们的completionadvisouster和advisouster组件。
无论如何,一个简单的自动补全功能仍然可以实现,只使用对象缓存和数据库。您只需要一个辅助方法来为每个字母组合创建适当的"缓存键"。例如: function createKeyByQuery($str)
{
return 'autocomplete-prefix-'.(string) $str;
}
和在suggest()
方法中:
public function suggest($keyword)
{
$key = $this->createKeyByQuery($keyword);
if($this->cache->hasItem($key)) {
return $this->cache->getItem($key);
}
// fetch form the database here
$data = $this->db->query();
$this->cache->setItem($key, $data);
return $data;
}
如果过滤器的数量不是太多,那么就让它们也成为键的一部分。在这个场景中,建议方法的签名是:
public function suggest($keyword, array $filters = []);
和密钥生成器需要更新:
function createKeyByQuery($str, array $filters = [])
{
return 'autocomplete-prefix-' . (string) $str . md5(serialize($filters));
}
此解决方案可能不适合复杂的/域相关数据,因为它具有相当大的无效挑战。如。你如何在有效载荷中找到保存"阿根廷"的缓存键?
由于您只处理国家和大陆列表作为过滤器,因此应该可以解决这个问题。
对于england
关键字和两个不同的过滤器,共有10个过滤器选项,将有10x2x7 = 140个不同的缓存键(s)。对于具有5个选项的单个过滤器,5x1x7 = 37个不同的键。
APCu是这个实现的一个很好的选择。
希望能有所帮助。