简体   繁体   English

在ODM中加载相关文档会导致查询过多

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

I'm stuck trying to reduce the number of database queries on a web api. 我一直在努力减少Web API上的数据库查询数量。 My database has 3 collections : playground , widget , token 我的数据库有3个馆藏: playgroundwidgettoken

One playground has many widgets, one widget has one token. 一个游乐场有很多小部件,一个小部件有一个令牌。 Each relationship uses referencesOne / referenceMany . 每个关系都使用referencesOne / referenceMany

So here are my simplified models 所以这是我的简化模型

/**
 * @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;
}

I need to use the full playground with all its widgets and tokens but by default, Doctrine does too many queries : one to get the playground (ok), one to get all widgets of the mapping (ok) and for each widget, one query to get the token (not ok). 我需要使用带有其所有小部件和令牌的完整游乐场,但默认情况下,Doctrine会执行过多的查询:一个要获取游乐场(确定),一个要获取映射的所有小部件(确定),而对于每个小部件,一个查询获取令牌(不好)。 Is there a way to query all tokens at once instead of getting them one by one ? 有没有一种方法可以一次查询所有令牌,而不是一个一个地获取它们?

I've looked at prime but it does not seem to solve my problem... 我已经看过黄金了,但似乎并不能解决我的问题...

Is there a way other than using the query builder and manually hydrate all objects to reduce the query count ? 除了使用查询生成器并手动合并所有对象以减少查询数量之外,还有其他方法吗?

Edit : As I added in my comment, what I'm looking for is get the playground and all its dependencies as a big object, json encode it and return it into the response. 编辑:正如我在评论中添加的那样,我正在寻找的是将游乐场及其所有依赖项作为一个大对象获取,并对其进行json编码并将其返回到响应中。

What I do for now is query the playground and encode it but Doctrine populates the dependencies in a non efficient way : first there is the query to get the playgroung, then, there is one more query to get the related widgets and there is one query for each widget to get its token. 我现在要做的是查询操场并对其进行编码,但是Doctrine以一种非有效的方式填充依赖项:首先是要获取Playgroung的查询,然后是又一个查询以获取相关的小部件,还有一个查询为每个小部件获取其令牌。

As one playground can have hundreds of widgets, this leads to hundreds of database queries. 一个游乐场可以包含数百个小部件,因此会导致数百个数据库查询。

What I'm looking for is a way to tell Doctrine to get all this data using only 3 queries (one to get the playgroung, one to get the widgets and one to get the tokens). 我正在寻找的是一种告诉Doctrine仅使用3个查询(一个用于获取playgroung,一个用于获取小部件以及一个用于获取令牌)来获取所有数据的方法。

update : since the ArrayCollection in the $playground should contain all the widgets at least as proxy objects (or should get loaded when accessed), the following should work to fetch all required tokens... 更新 :由于$playground的ArrayCollection应该至少包含所有小部件作为代理对象(或者应该在访问时加载),因此以下应该可以工作以获取所有必需的令牌...

Since the document manager keeps all managed objects, it should prevent additional queries from occuring. 由于文档管理器保留所有托管对象,因此防止发生其他查询。 (Notice the omitted assignment from the execute ). (请注意execute省略的分配)。

$qb = $dm->createQueryBuilder('Token')->findBy(['widget' => $playground->getWidgets()]);
$qb->getQuery()->execute();

inspired by this page on how to avoid doctrine orm traps - point 5 受此启发, 了解如何避免教义性陷阱-第5点

old/original answer 旧/原始答案

I'm not quite familiar with mongodb, tbh, but according to doctrine's priming references , you should be able to somewhat comfortably hydrate your playground by: 我对mongodb,tbh不太熟悉,但是根据doctrine的主要参考资料 ,您应该可以通过以下方式为您的操场加水:

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

however, I might be so wrong. 但是,我可能错了。

What about that: 那个怎么样:

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();
    }
}

At least with mysql backend, this solves the "n+1 problem". 至少在mysql后端,这解决了“ n + 1问题”。 Not sure about MongoDB, though. 不过,不确定MongoDB。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM