【问题标题】:Calculating product counts (efficiently) in faceted search with PHP, MySQL使用 PHP、MySQL 在分面搜索中(有效地)计算产品数量
【发布时间】:2014-12-18 16:06:18
【问题描述】:

假设我有一个页面,显示网上商店中所有可用的笔记本电脑。这些结果可以通过激活某些过滤器来优化,例如品牌、价格、重量、内存量、磁盘空间,如下所示:

Brand
[ ] Dell (16)
[ ] HP (12)
[ ] Lenovo (9)

Memory
( ) ≥ 2 GB (37)
( ) ≥ 4 GB (24)
( ) ≥ 8 GB (8)

Price
( ) ≤ 200 $ (3)
( ) ≤ 300 $ (12)
( ) ≤ 500 $ (22)

目前,我正在使用单个查询计算这些产品计数,我在 PHP 中循环遍历其结果以将它们划分为这些类别(即“内存,≥ 4 GB”)。这种方法的问题是选择例如“≥4GB”并且显示结果时,产品数量变得不合逻辑。

MySQL(简体):

SELECT brand, memory, price FROM Laptops WHERE memory >= 4000

PHP(简体):

while($row = msqli_fetch_assoc($results)) {
    if($row['memory'] >= 2000) {
        $product_count['memory']['2gb']++;
    }
}

结果:

Memory
( ) ≥ 2 GB (24) <-- should be 37
(o) ≥ 4 GB (24)
( ) ≥ 8 GB (8)

因为现在我正在使用带有WHERE memory &gt;= 4000 之类条件的查询,所以产品计数仅限于当前的 24 个结果(与激活的过滤器匹配),因此“≥ 2 GB”现在的计数为 24也是。确实,在 24 个结果中,24 个将具有 2 GB 或更多内存,但这与用户无关,用户需要知道如果选择 2 GB 将显示多少个结果。

我可以分别查询每个过滤器的数据库,忽略该过滤器的WHERE 条件,以便其产品计数不受其自身设置的影响,但仍受任何其他过滤器(如品牌)的影响。但是,我计划至少有十几个过滤器,这样会导致十几个查询,给原本相当简单的应用程序带来相对较大的额外压力。

是否有更有效的方法来计算产品数量,所需的查询更少?

【问题讨论】:

  • 也许在 php 上 -> if($row['memory'] >= 2000 && $row['memory']
  • 当您已经使用 php 过滤结果时,为什么还要在 sql 查询中使用 where ?使用您当前的 sql 查询,不会有任何 2gb 内存,因此 2gb 内存笔记本电脑的计数为 0。
  • @MarcoMura 问题是我正在使用有限的 MySQL 结果集。
  • @redburn 受什么限制?
  • 听起来你需要一个单独的查询

标签: php mysql faceted-search


【解决方案1】:

您需要在用户选择的任何过滤器之外构建分面查询,因为分面计数永远不会改变。您可能不会这样做,因此当用户选择 4GB 时,您的查询变为:

SELECT brand, memory, price FROM Laptops WHERE memory >= 4000

什么时候应该:

SELECT brand, memory, price FROM Laptops WHERE memory >= 2000

我建议您重新编写构面逻辑以不考虑过滤器并优化查询以进行计数,而不是在 PHP 中进行:

SELECT count(*) as total from Laptops WHERE memory >= 2000;
SELECT count(*) as total from Laptops WHERE memory >= 4000;

唉,如果您的用户选择了多个过滤器,它将变得更加复杂,因为您可能确实希望在查询中考虑不在同一方面部分中的其他过滤器。

所以...如果按品牌分面,则仅使用此查询来记忆:

SELECT count(*) as total from Laptops WHERE memory >= 2000 and brand in ('HP', 'Dell');
SELECT count(*) as total from Laptops WHERE memory >= 4000 and brand in ('HP', 'Dell');

但是对于相同结果集的品牌方面,不要考虑内存,因为它没有被选中:

SELECT count(*) as total from Laptops WHERE brand = 'HP';
SELECT count(*) as total from Laptops WHERE brand = 'Dell';
SELECT count(*) as total from Laptops WHERE brand = 'Lenovo';

唉,如果按品牌和记忆过滤,请考虑价格:

SELECT count(*) as total from Laptops WHERE price < 200 and memory between 2000 and 4000 and brand in ('HP', 'Dell);

是的,把所有事情都做好可能会很棘手,尤其是考虑到我没有提到您可能只希望为价格选择一个选项而为品牌选择多个选项的情况。这就是为什么大多数人会转向 http://lucene.apache.org/solr/ 进行分面搜索,它只返回由您要求的任何字段分面的结果集。

【讨论】:

  • 感谢您的宝贵时间,您看到了我的困境。我研究了 Solr,但仅仅为了这些产品数量而部署它显然是矫枉过正的。我现在找到了一种使用 PHP/MySQL 的方法来(重新)计算仅针对活动过滤器的计数,因为正如您正确地说的那样,我“想考虑不在同一方面部分的查询中的其他过滤器”。
【解决方案2】:

我的想法的错误在于,我需要对每个可能的过滤器进行额外查询,以准确计算产品数量。但是,计数仅对于当前选择的过滤器(如上面的内存)中的(无线电)选项不准确,因此,如果我仅重新计算活动过滤器的产品计数(通过检查当前的 $_GET 参数),则会大大减少数量所需的查询。

所以我将有一个包含所有过滤器的查询,例如:

$query = 'SELECT id, brand, memory, price, stock FROM Laptops 
          WHERE stock > 0 AND memory >= 4000 AND price <= 200';

我将使用 PHP 循环,将结果划分为过滤器类别并计算其计数,然后对于每个活动过滤器,我将重置这些计数并根据查询重新计算它们该过滤器已被 preg_replace('(AND memory &gt;= \d+)','',$query); 之类的正则表达式删除。

$query = 'SELECT id, brand, memory, price, stock FROM Laptops 
          WHERE stock > 0 AND price <= 200';

基于该查询,内存计数现在将准确反映每个选项的结果,同时仍考虑设置的任何其他过滤器(即超出内存过滤器的范围):

Memory
( ) ≥ 2 GB (37)
(o) ≥ 4 GB (24)
( ) ≥ 8 GB (8)

现在用户知道 4 GB 将列表限制为 24 个项目,切换到 2 GB 会产生更多结果。

如果没有过滤器处于活动状态,我不需要任何额外的查询来计算产品数量。如果用户确实应用了一个或多个过滤器,我只需要为每个过滤器添加一个额外的查询,我对此很满意。

【讨论】:

    【解决方案3】:

    我希望问题可能是查询让我们将查询更改为下面提到的并执行它。谢谢

    SELECT brand, memory, price FROM Laptops WHERE memory >= 2000
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-19
      • 2012-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多