按 One-to-Many 个关联值搜索而不限制剩余值
Search by One-to-Many Association values without limiting the remaining values
背景
我的客户希望根据类别构建一个包含许多不同类型属性的产品目录。客户还希望具有相当全面的搜索功能,可以检查产品名称及其属性。所需的显示格式按类别分组,然后是 table 与搜索词匹配的产品。
压缩实体概述
Category
- name (string)
- products (1-to-Many:Product)
- attributes (1-to-Many:Attribute)
Attribute
- name (string)
- isDropdown (bool) - flag meaning can either be custom or from the dropdown options
- attributeOptions (1-to-Many:AttributeOption)
AttributeOption
- attribute (Many-to-1:Attribute)
- value
ProductAttributeValue
- attribute (Many-to-1:Attribute)
- selectedOption (Many-to-1:AttributeOption)
- value (string)
Product
- name (string)
- attributeValues (1-to-Many:ProductAttributeValue)
- category (Many-to-1:Category)
问题
按产品名称搜索非常简单:
$search = 'gloves and stuff';
$searchTerms = explode(' ', $search);
$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
;
$i = 0;
foreach ($searchTerm as $st) {
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}
$categoriesWithProducts = $categoriesWithProducts->getQuery()->getResult();
并使用此 Twig 代码实现输出:
{% for category in categoriesWithProducts %}
<h2>{{ category.name }}</h2>
<table>
<thead>
{% for attribute in category.attributes %}
<th>{{ attribute.name }}</th>
{% endfor %}
</thead>
<tbody>
{% for product in category.products %}
<tr>
<td>{{ product.name }}</td>
{% for product in category.attributes %}
<td>
{% for attributeValue in product.attributeValues if attributeValue.attribute == attribute %}
{{ attribute.isDropdown ? attributeValue.selectedOption.value : attributeValue.value }}
{% endfor %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endfor %}
结果输出:
然而,当我修改查询以匹配搜索词的属性时:
$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
;
$i = 0;
foreach ($searchTerm as $st) {
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->orWhere('av.value LIKE ?'.$i)
->orWhere('ao.value LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}
搜索仍然适用于匹配的产品名称,但是当从 [=18= 中找到匹配项时, 会生成 attributeValues
和 selectedOption
的子集] 学期。例如,$search = 'red'
:
和$search = 'red small'
:
我尝试修复
我尝试对两个关联实体使用两个连接,但最终在我的结果集中得到了重复项,这在我的 Twig 渲染中产生了双重输出。
代码:
$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao, avSearch, aoSearch')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
->leftJoin('p.attributeValues', 'avSearch')
->leftJoin('av.selectedOption', 'aoSearch')
;
$i = 0;
foreach ($searchTerm as $st) {
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->orWhere('avSearch.value LIKE ?'.$i)
->orWhere('aoSearch.value LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}
结果 $search = 'gloves and stuff'
:
以及 $search = 'red small'
(注意重复的 Red
和 Small
条目,但其他属性的结果是单一的):
我尝试了多种方法来隐藏 aoSearch
和 avSearch
结果:
- 将它们别名为
HIDDEN aoSearch
和 HIDDEN avSearch
什么都不做
- 从
SELECT
中删除 aoSearch
和 avSearch
会恢复到第二个和第三个屏幕截图中发现的损坏的子集问题。
问题
如果在产品的任何属性中找到匹配项,我希望获得 table 中显示的产品的所有属性。 有什么方法可以隐藏我错过的 aoSearch
和 avSearch
的比赛吗?
发布这个问题后仅半小时,我就有了解决方法:
到目前为止,我找到解决方案的唯一方法是在单个 orWhere
子句中使用 EXISTS
函数,以便匹配包含在子查询中:
$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
;
$i = 0;
$subQuery = '';
foreach ($searchTerm as $st) {
if ($i > 0) $subQuery .= ' OR ';
$subQuery = 'sao.text LIKE ?'.$i.' OR pav.text LIKE ?'.$i;
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}
$categoriesWithProducts->orWhere(
'EXISTS (
SELECT pav
FROM AcmeBundle:ProductAttributeValue pav
LEFT JOIN pav.selectedOption sao
WHERE pav.product = p AND ('.$subQuery.'))
');
$categoriesWithProducts = $categoriesWithProducts->getQuery()->getResult();
这为我提供了具有丰富属性的完美搜索结果!
背景
我的客户希望根据类别构建一个包含许多不同类型属性的产品目录。客户还希望具有相当全面的搜索功能,可以检查产品名称及其属性。所需的显示格式按类别分组,然后是 table 与搜索词匹配的产品。
压缩实体概述
Category
- name (string)
- products (1-to-Many:Product)
- attributes (1-to-Many:Attribute)
Attribute
- name (string)
- isDropdown (bool) - flag meaning can either be custom or from the dropdown options
- attributeOptions (1-to-Many:AttributeOption)
AttributeOption
- attribute (Many-to-1:Attribute)
- value
ProductAttributeValue
- attribute (Many-to-1:Attribute)
- selectedOption (Many-to-1:AttributeOption)
- value (string)
Product
- name (string)
- attributeValues (1-to-Many:ProductAttributeValue)
- category (Many-to-1:Category)
问题
按产品名称搜索非常简单:
$search = 'gloves and stuff';
$searchTerms = explode(' ', $search);
$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
;
$i = 0;
foreach ($searchTerm as $st) {
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}
$categoriesWithProducts = $categoriesWithProducts->getQuery()->getResult();
并使用此 Twig 代码实现输出:
{% for category in categoriesWithProducts %}
<h2>{{ category.name }}</h2>
<table>
<thead>
{% for attribute in category.attributes %}
<th>{{ attribute.name }}</th>
{% endfor %}
</thead>
<tbody>
{% for product in category.products %}
<tr>
<td>{{ product.name }}</td>
{% for product in category.attributes %}
<td>
{% for attributeValue in product.attributeValues if attributeValue.attribute == attribute %}
{{ attribute.isDropdown ? attributeValue.selectedOption.value : attributeValue.value }}
{% endfor %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endfor %}
结果输出:
然而,当我修改查询以匹配搜索词的属性时:
$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
;
$i = 0;
foreach ($searchTerm as $st) {
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->orWhere('av.value LIKE ?'.$i)
->orWhere('ao.value LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}
搜索仍然适用于匹配的产品名称,但是当从 [=18= 中找到匹配项时, 会生成 attributeValues
和 selectedOption
的子集] 学期。例如,$search = 'red'
:
和$search = 'red small'
:
我尝试修复
我尝试对两个关联实体使用两个连接,但最终在我的结果集中得到了重复项,这在我的 Twig 渲染中产生了双重输出。
代码:
$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao, avSearch, aoSearch')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
->leftJoin('p.attributeValues', 'avSearch')
->leftJoin('av.selectedOption', 'aoSearch')
;
$i = 0;
foreach ($searchTerm as $st) {
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->orWhere('avSearch.value LIKE ?'.$i)
->orWhere('aoSearch.value LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}
结果 $search = 'gloves and stuff'
:
以及 $search = 'red small'
(注意重复的 Red
和 Small
条目,但其他属性的结果是单一的):
我尝试了多种方法来隐藏 aoSearch
和 avSearch
结果:
- 将它们别名为
HIDDEN aoSearch
和HIDDEN avSearch
什么都不做 - 从
SELECT
中删除aoSearch
和avSearch
会恢复到第二个和第三个屏幕截图中发现的损坏的子集问题。
问题
如果在产品的任何属性中找到匹配项,我希望获得 table 中显示的产品的所有属性。 有什么方法可以隐藏我错过的 aoSearch
和 avSearch
的比赛吗?
发布这个问题后仅半小时,我就有了解决方法:
到目前为止,我找到解决方案的唯一方法是在单个 orWhere
子句中使用 EXISTS
函数,以便匹配包含在子查询中:
$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
;
$i = 0;
$subQuery = '';
foreach ($searchTerm as $st) {
if ($i > 0) $subQuery .= ' OR ';
$subQuery = 'sao.text LIKE ?'.$i.' OR pav.text LIKE ?'.$i;
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}
$categoriesWithProducts->orWhere(
'EXISTS (
SELECT pav
FROM AcmeBundle:ProductAttributeValue pav
LEFT JOIN pav.selectedOption sao
WHERE pav.product = p AND ('.$subQuery.'))
');
$categoriesWithProducts = $categoriesWithProducts->getQuery()->getResult();
这为我提供了具有丰富属性的完美搜索结果!