我一直在努力减少Web api上的数据库查询数量。
我的数据库有3个集合:playground
,widget
,token
一个游乐场有很多小部件,一个小部件有一个令牌。每个关系都使用referencesOne
/ referenceMany
。
这是我的简化模型
/**
* @MongoDB\Document()
*/
class Widget
{
/**
* @MongoDB\ReferenceOne(targetDocument="Token", inversedBy="widgets")
*/
protected $token;
/**
* @MongoDB\ReferenceOne(targetDocument="Playground", inversedBy="widgets")
*/
protected $playground;
}
/**
* @MongoDB\Document()
*/
class Playground
{
/**
* @MongoDB\ReferenceMany(targetDocument="Widget", mappedBy="playground")
*/
protected $widgets;
}
/**
* @MongoDB\Document()
*/
class Token
{
/**
* @MongoDB\ReferenceMany(targetDocument="Widget", mappedBy="token")
*/
protected $widgets;
}
我需要使用完整的游乐场及其所有小部件和令牌,但默认情况下,Doctrine会进行过多的查询:一个查询获取游乐场(确定),一个查询获取映射的所有小部件(确定),并且每个小部件,一次查询以获取令牌(不好)。有没有一种方法可以一次查询所有令牌,而不是一个一个地获取它们?
我看过素数,但似乎并不能解决我的问题...
除了使用查询生成器并手动合并所有对象以减少查询数量之外,还有其他方法吗?
编辑: 正如我在评论中所添加的那样,我要寻找的是将游乐场及其所有依赖项作为一个大对象进行json编码,然后将其返回到响应中。
我现在要做的是查询操场并对其进行编码,但是Doctrine以一种非有效的方式填充依赖项:首先是查询以获取Playgroung,然后是另一个查询以获取相关小部件,然后是每个小部件获取其令牌的一个查询。
由于一个游乐场可以包含数百个小部件,因此会导致数百个数据库查询。
我正在寻找一种方法来告诉Doctrine仅使用3个查询(一个用于获取playgroung,一个用于获取小部件,一个用于获取令牌)来获取所有这些数据。
答案 0 :(得分:1)
更新:由于$playground
中的ArrayCollection至少应包含所有小部件作为代理对象(或在访问时加载),因此以下内容应可用于获取所有必需的令牌...
由于文档管理器保留了所有托管对象,因此应该防止发生其他查询。 (请注意,execute
中省略了分配)。
$qb = $dm->createQueryBuilder('Token')->findBy(['widget' => $playground->getWidgets()]);
$qb->getQuery()->execute();
受this page on how to avoid doctrine orm traps - point 5启发
旧/原始答案
我对mongodb,tbh不太熟悉,但是根据教义的priming references,您应该可以通过以下方式为您的游乐场加水:
$qb = $dm->createQueryBuilder('Widget')->findBy(['playground' => $playground]);
$qb->field('token')->prime(true);
$widgets = $qb->getQuery()->execute();
但是,我可能错了。
答案 1 :(得分:0)
那又怎么样:
class Foo
{
/** @var \Doctrine\ORM\EntityManagerInterface */
private $entityManager;
public function __construct(\Doctrine\ORM\EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function getAll(): array {
$qb = $this->entityManager->createQueryBuilder();
return $qb
->select('p,t,w')
->from(Playground::class, 'p')
->join(Widget::class, 'w')
->join(Token::class, 't')
->getQuery()
->getResult();
}
}
至少对于mysql后端,这解决了“ n + 1问题”。不过,不确定MongoDB。