【问题标题】:Doctrine 2 find pages having attributesDoctrine 2 查找具有属性的页面
【发布时间】:2014-07-24 14:04:58
【问题描述】:

我需要用页面属性的过滤条件填充查询生成器。我得到了实体“页面”、“属性”和“值”

Page: id, name, etc
Attribute: id, name, etc
Value: page_id, date, string, numeric

我需要获取所有在表单中选择属性值的页面。

所以我得到了查询生成器:

$qb->select('p')
   ->from(Page p)
   ->leftJoin(p.values);

我可以使用“have”或“where”子句来做like吗?

$qb->add('having', 'a.id = :attr1_id AND a.value = :attr1_val')
$qb->add('having', 'a.id = :attr2_id AND a.value = :attr2_val')

更新:不,我不能。这样就可以得到 attr1_id = attr2_value AND attr2_id = attr1_value 的页面,并且所有条件都为真,但结果是错误的。

或者我应该为每个值添加自定义连接?

更新:这是工作示例:

//attr1.intval=:a1 (or LIKE, BETWEEN, etc compare)
$comp_expression=$aliace.'.'.$value_field.'=:a'.$this->getId();
//INNER JOIN attribute attr1 ON attr1.type=5 AND attr1.intval=:a1
$qb->innerJoin($value_class, $aliace , 'WITH', $aliace.'.type='.$this->getId().' AND '.$comp_expression);
//where attr1.intval IS NOT NULL
$qb->andWhere($aliace.'.'.$value_field.' IS NOT NULL');
$qb->setParameter('a'.$this->getId(),$value);

更新:所以进行键+值过滤的唯一方法是为每个过滤条件添加连接?我现在有 27 个属性,所以需要用 27 个连接构建查询吗?

还有其他更好的方法吗?抱歉,如果这是重复的,找不到关键字来查找相同的问题。

更新:也许我应该摆脱 sql 查询限制并为它创建 mysql 过程?

更新:我可以在教义中使用这样的东西吗? MySQL optimization on filtering key-value pairs as records

【问题讨论】:

  • 您在搜索完全匹配给定属性的页面吗?包含 all 的给定属性;匹配 any 的给定属性;还是逻辑更复杂?
  • 只匹配所有给定的属性
  • 但值不能是 exec 匹配,而是 BETWEEN 表达式或 %LIKE%。 “values”表有日期、字符串和数字字段,在“属性”类中指定映射到值中的真实字段名称,所以它不是问题。

标签: php mysql sql doctrine-orm dql


【解决方案1】:

型号

您在此处使用的模型称为Entity-Attribute-Value (EAV)。如果您不知道这一点,我建议您先阅读有关利弊的信息。

其中一个缺点是在 EAV 模型中进行搜索很困难而且效率很低。

AND 查询

您似乎想要查询具有特定属性的特定值特定属性的另一个特定值的实体。这些类型的查询在 EAV 模型中不是非常有效。

假设您的实体设置为:

Entity:    id, attributes (one-to-many), values (one-to-many)
Attribute: id, name, entity (many-to-one), values (one-to-many)
Value:     id, content, entity (many-to-one), attribute (many-to-one)

假设您要查询具有属性color 和值blue 的所有实体。一个 Doctrine 查询看起来像这样:

SELECT e FROM Entity e
JOIN e.values v JOIN v.attribute a
WHERE a.name = 'color' AND v.content = 'blue'

(当然你不应该像这样在 DQL 中使用值,而是将它们绑定为参数。)

现在假设您要查询所有具有属性color 和值blue 属性shape 和值square 的实体。查询变为:

SELECT e FROM Entity e
JOIN e.values v1 JOIN v.attribute a1
JOIN e.values v2 JOIN v.attribute a2
WHERE a1.name = 'color' AND v1.content = 'blue'
AND a2.name = 'shape' AND v2.content = 'square'

现在,如果您想查询 3 个属性/值对,则需要其中 3 个 JOIN 集。如果要查询 4 ​​个属性/值对,则需要其中的 4 个 JOIN 集。等等。

OR 查询

假设您要查询具有属性color 和值blue 属性shape 和值square 的所有实体。查询变为:

SELECT e FROM Entity e
JOIN e.values v JOIN v.attribute a
WHERE (a.name = 'color' AND v.content = 'blue')
OR (a.name = 'shape' AND v.content = 'square')

您可以看到这更有效,您不需要为每个额外的属性/值对添加那些额外的 JOIN 集。

混合与或

我要简短地说:这将成为查询的噩梦。我强烈建议您不要这样做。

另类

如果您的应用程序将严重依赖这些类型的查询(尤其是 AND 和混合 AND OR),我建议您考虑使用不同类型的存储引擎。关系数据库并不适合这种事情。

使用Document Oriented Database 可能会更好,例如ElasticsearchMongoDBCouchDB 等。

混合替代

您还可以将 EAV 部分“复制”到面向文档的数据库中,并将其仅用于搜索功能。我建议您在对实体进行更改(创建、更新、删除)时设置事件。然后创建将这些更改持久保存到文档存储中的侦听器。

这样,您的应用程序可以使用关系数据库进行正常操作,使用文档存储进行搜索。在这种情况下我建议Elasticsearch,它非常适合这种事情。

【讨论】:

  • 谢谢!在我对这种结构的了解中,您填充了空格。这对我来说是非常有用的信息。我无法将项目移动到 nosql 数据库,希望我永远不会被要求使用 AND/OR 混合构建查询。
  • 我并不是建议您将整个关系数据库移动到文档数据库,只是使用 EAV 模型的部分;)或者您可以将该部分“复制”到文档数据库并仅使用它用于搜索(我会更新我的答案)。
  • 我可以将属性转换为 json 属性字段并用于过滤,我认为这正是我需要的,因为所有页面都在 sphinx 索引中用于搜索我只需要创建 EAV 到 JSON 的转换,非常感谢!
【解决方案2】:

这两种方法没有这样的区别,两种方法做的事情完全一样。

通过问题的中心点,我怀疑是关于使用 like 构造查询的问题。

您提供的第一个示例是有效的。 你提出的第二个例子是不对的。

在第二个示例中,您需要在 andWhere 方法上定义所有 where 子句。

$qb->innerJoin('a1 ON p.id=a1.page_id');
$qb->andWhere('a1.attribute_id=:attr1_id ');
$qb->andWhere('a1.value=:attr1_val');
$qb->andWhere('a1 is not null');

你所做的只是通过第三个api创建一个sql,Doctrine ORM是php最成功的orm工具之一,知道如何使用它会让你体验ORM的力量,通过doctrine实现你所需要的一切知道有:

Doctrine Create Query Builder -> 你正在使用相同的。

Doctrine Query Builder

Doctrine 创建查询 -> 最简单的形式

Query Language

【讨论】:

  • “有”子句远不一样。至于 mysql,其绝对不同的操作使它们在不同的阶段处理并使用不同的数据集。 “Where”子句适用于目录,但“Having”适用于结果表。当我们可以使用 ON 运算符过滤连接阶段的数据时,在“Where”子句中过滤连接表内容也是一个坏主意(这是连接和多表查询之间的性能差异)。但无论如何,谢谢,你把我推到了正确的方向。
  • 也许您应该尝试阅读这些文档并重新考虑您的问题。
猜你喜欢
  • 2011-03-09
  • 1970-01-01
  • 1970-01-01
  • 2014-01-15
  • 1970-01-01
  • 1970-01-01
  • 2010-10-17
  • 1970-01-01
相关资源
最近更新 更多