doctrine ODM 加载相关文档导致查询过多

loading of related documents in doctrine ODM leads to too many queries

我一直在尝试减少 Web 上的数据库查询数量 api。 我的数据库有 3 个集合:playgroundwidgettoken

一个playground有很多widget,一个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;
}

我需要使用完整的 playground 及其所有小部件和标记,但默认情况下,Doctrine 进行了太多查询:一个获取 playground (ok),一个获取映射的所有小部件 (ok) 和每个小部件,一个获取令牌的查询(不好)。有没有办法一次查询所有令牌而不是一个一个地获取它们?

我看过 prime,但它似乎没有解决我的问题...

除了使用查询生成器并手动混合所有对象以减少查询计数之外,还有其他方法吗?

编辑: 正如我在评论中添加的那样,我正在寻找的是将游乐场及其所有依赖项作为一个大对象,json 对其进行编码并将其 return 放入响应中。

我现在做的是查询 playground 并对其进行编码,但 Doctrine 以一种非高效的方式填充依赖关系:首先是获取 playgroung 的查询,然后是另一个获取相关小部件的查询每个小部件都有一个查询来获取其令牌。

由于一个游乐场可以有数百个小部件,这会导致数百个数据库查询。

我正在寻找的是一种告诉 Doctrine 仅使用 3 个查询(一个用于获取 playgroung,一个用于获取小部件,一个用于获取令牌)获取所有这些数据的方法。

update:因为 $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

old/original 回答

我不太熟悉 mongodb,说实话,但根据学说 priming references,您应该可以通过以下方式轻松地为您的游乐场补水:

$qb = $dm->createQueryBuilder('Widget')->findBy(['playground' => $playground]);
$qb->field('token')->prime(true);
$widgets = $qb->getQuery()->execute();

然而,我可能错了。

那个呢:

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 problem"。不过不确定 MongoDB。