Propel NestedSet创建平衡树

时间:2009-11-11 06:06:19

标签: symfony1 propel nested-sets

我正在尝试使用Propel的NestedSet功能。但是,我遗漏了一些关于插入的问题,即树在创建时是平衡的(即水平填充)。

说我有这些元素:

       root
  r1c1      r1c2
r2c1 r2c2

我想插入r2c3作为r1c2的第一个子节点(即在第3行开始之前填充第2行)。

我的第一个尝试就是创建这个功能:

function where(User $root,$depth=0)
{
  $num = $root->getNumberOfDescendants();
  if ( $num < 2 )
    return $root;
  foreach($root->getChildren() as $d)
  {
    if ( $d->getNumberOfChildren() < 2 )
    {
      return $d;
    }
  }
  foreach($root->getChildren() as $d)
  {
    return where($d, $depth+1);
  }
}

但是,这会在r2c1上插入一个孩子,而不是我想要的r1c2。

有没有办法以某种方式在下一个可用位置插入树?

TIA 麦克

1 个答案:

答案 0 :(得分:0)

好的,感谢http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/,我发现这个算法会做我想要的:

function where($root)
{
  $num = $root->getNumberOfDescendants();
  if ( $num < 2 )
    return $root;

  $finder = DbFinder::from('User')->
    where('LeftId','>=',$root->getLeftId())->
    where('RightId','<=',$root->getRightId())->
    whereCustom('user.RightId = user.LeftId + ?',1,'left')->
    whereCustom('user.RightId = user.LeftId + ?',3,'right')->
    combine(array('left','right'),'or')->
    orderBy('ParentId');
    return $finder->findOne();
}

它基本上执行这个SQL:

SELECT u.*
FROM user u
WHERE u.LEFT_ID >= $left AND u.RIGHT_ID <= $right AND
  (u.RIGHT_ID = u.LEFT_ID+1 OR u.RIGHT_ID = u.LEFT_ID+3)
ORDER BY u.PARENT_ID
LIMIT 1

一片叶子有RIGHT = LEFT + 1,一个有一个孩子的节点有RIGHT = LEFT + 3。通过添加ORDER BY u.PARENT_ID,我们可以找到树中可用的最高节点。如果您使用LEFT_ID或RIGHT_ID,则不会平衡树。