【问题标题】:Efficiently check if document has children有效检查文档是否有孩子
【发布时间】:2015-03-12 09:03:48
【问题描述】:

我正在尝试使用 Doctrine MongoDB 构建延迟加载树。 我的文档结构如下:

/**
 * @ODM\Document(repositoryClass="CmsPage\Repository\PageRepository")
 */
class Page
{
    /**
     * @ODM\String
     * @var string
     */
    protected $title;

    /**
     * @ODM\ReferenceOne(targetDocument="CmsPage\Document\Page", inversedBy="children")
     * @ODM\Index
     * @var Page
     */
    protected $parent;

    /**
     * @ODM\ReferenceMany(
     *     targetDocument="CmsPage\Document\Page", mappedBy="parent",
     *     sort={"title": "asc"}
     * )
     * @var array
     */
    protected $children;

    /**
     * Default constructor
     */
    public function __construct()
    {
        $this->children = new ArrayCollection();
    }

    /**
     * @return ArrayCollection|Page[]
     */
    public function getChildren()
    {
        return $this->children;
    }

    /**
     * @param ArrayCollection $children
     */
    public function setChildren($children)
    {
        $this->children = $children;
    }

    /**
     * @return Page
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * @param Page $parent
     */
    public function setParent($parent)
    {
        $this->parent = $parent;
    }

    /**
     * @return string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * @param string $title
     */
    public function setTitle($title)
    {
        $this->title = $title;
    }
}

以下代码将检索给定页面的所有子页面:

$page = $pageRepo->find('foo');
$children = [];

foreach ($page->getChildren() as $childPage) {
    $children[] = [
        'id' => $childPage->getId(),
        'slug' => $childPage->getSlug(),
        'leaf' => ($childPage->getChildren()->count() == 0)
    ];

这按预期工作,但将为每个子页面执行单独的查询以检查它是否为叶子。在处理具有大量子节点的大树时,效率将不高。

我可以在我的 Page 文档中引入一个布尔值 isLeaf 并在持久化时更新它。但这也意味着我必须在添加或删除孩子时更新父母。

你有解决这个问题的建议吗?

【问题讨论】:

    标签: php mongodb mongodb-query doctrine-mongodb


    【解决方案1】:

    我知道在 MongoDB 中测试数组不为空的最有效方法是使用 "dot notation"$exists 搜索数组中是否存在“第一个”元素。在query builder 中有访问权限:

    $qb = $dm->createQueryBuilder('Page')
        ->field('children.0')->exists(true);
    

    这和shell中的这个是一样的:

    db.collection.find({ "children.0": { "$exists": true } })
    

    所以0 是数组中第一个元素的索引,并且仅在该数组中有一些内容时才存在。空数组不符合此条件。

    【讨论】:

    • 感谢您的回答。我有 2 个顾虑。这只会检索至少有一个孩子的所有页面。我需要所有页面和一个指示页面是否有子级的标志。此外,此方法仍会触发 imo 对所有页面的 1*n 查询,但我需要先对其进行测试。
    • 我不能使用这种方法,因为我已经使用parent references 对树进行了建模。我的文档在 MongoDB 中只有一个 parent 字段。
    • @BramGerritsen 您的模型没有将孩子定义为数组吗?看起来是这样。这也是合乎逻辑的事情。
    • 这是教义魔法。 ReferenceMany 和 mappedBy 父级。这将允许您使用延迟加载检索子项。 MongoDB 有一个reference,它描述了不同的树实现。您可以选择父引用的八个子引用,但不能同时选择两者。父引用最适合我的用例,因为它很容易实现,而子引用变化很大,因此其他建模选项将更难实现。
    • @BramGerritsen Sooo 这就是问题所在。您需要找到孩子作为您的常用使用模式。你 A. 为每个孩子存储一个父引用,并根据当前文档节点的当前值(大量查询)重新检查你的集合。或者 B. 存储可以从节点本身访问的直接子节点数组。我的观点是,这并不总是让解决方案适应你现有的,而是让你的设计适应最佳解决方案。
    猜你喜欢
    • 2017-03-19
    • 1970-01-01
    • 1970-01-01
    • 2015-04-24
    • 2023-03-12
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    • 2017-10-24
    相关资源
    最近更新 更多