I'm facing a problem with the nested set model, with MySQL. I can insert, delete, move subtrees to another parent, everything works fine.
But I can't figure out how to order siblings. For example, I have these siblings :
A, B, C, D, E
And I want to move B after D, obtaining this :
A, C, D, B, E
I found tons of stored procedures for inserting, deleting, etc. but not a single one to order siblings. The only one I found is a procedure for swapping siblings, but that's not what I want to achieve.
I tried to write my own one, but it seems complicated and doesn't work in all cases.
If you know how to move a node before or after one of his siblings, this would be greatly appreciated.
So... I re-wrote everything and here is a stored procedure that works well to move one node after one of his siblings. If we want to move the node to the first position, just pass the parent id in place of the sibling id.
DELIMITER |
-- sibling parameter is either :
-- - the sibling id after which we want to put the page
-- - the parent id if we want to put the page on the first position
CREATE PROCEDURE move_after_sibling (IN to_move_id INT(10), IN parent_id INT(10), IN sibling_id INT(10))
LANGUAGE SQL
DETERMINISTIC
BEGIN
DECLARE to_move_lft INT(10);
DECLARE to_move_rgt INT(10);
DECLARE parent_lft INT(10);
DECLARE parent_rgt INT(10);
DECLARE sibling_lft INT(10);
DECLARE sibling_rgt INT(10);
SET to_move_lft = (SELECT lft FROM pages WHERE id = to_move_id);
SET to_move_rgt = (SELECT rgt FROM pages WHERE id = to_move_id);
SET parent_lft = (SELECT lft FROM pages WHERE id = parent_id);
SET parent_rgt = (SELECT rgt FROM pages WHERE id = parent_id);
SET sibling_lft = (SELECT lft FROM pages WHERE id = sibling_id);
SET sibling_rgt = (SELECT rgt FROM pages WHERE id = sibling_id);
UPDATE pages
SET
lft =
CASE
WHEN sibling_id = parent_id THEN
CASE
WHEN lft BETWEEN parent_lft+1 AND to_move_lft-1 THEN
lft + (to_move_rgt - to_move_lft) + 1
WHEN lft BETWEEN to_move_lft AND to_move_rgt THEN
lft - (to_move_lft - (parent_lft + 1))
ELSE
lft
END
ELSE
CASE
WHEN to_move_lft > sibling_lft THEN
CASE
WHEN lft BETWEEN sibling_rgt AND to_move_lft-1 THEN
lft + (to_move_rgt - to_move_lft) + 1
WHEN lft BETWEEN to_move_lft AND to_move_rgt THEN
lft - (to_move_lft - (sibling_rgt + 1))
ELSE
lft
END
ELSE
CASE
WHEN lft BETWEEN to_move_rgt+1 AND sibling_rgt THEN
lft - ((to_move_rgt - to_move_lft) + 1)
WHEN lft BETWEEN to_move_lft AND to_move_rgt THEN
lft + (sibling_rgt - to_move_rgt)
ELSE
lft
END
END
END,
rgt =
CASE
WHEN sibling_id = parent_id THEN
CASE
WHEN rgt BETWEEN parent_lft+1 AND to_move_lft-1 THEN
rgt + (to_move_rgt - to_move_lft) + 1
WHEN rgt BETWEEN to_move_lft AND to_move_rgt THEN
rgt - (to_move_lft - (parent_lft + 1))
ELSE
rgt
END
ELSE
CASE
WHEN to_move_rgt > sibling_lft THEN
CASE
WHEN rgt BETWEEN sibling_rgt+1 AND to_move_lft-1 THEN
rgt + (to_move_rgt - to_move_lft) + 1
WHEN rgt BETWEEN to_move_lft AND to_move_rgt THEN
rgt - (to_move_lft - (sibling_rgt + 1))
ELSE
rgt
END
ELSE
CASE
WHEN rgt BETWEEN to_move_rgt+1 AND sibling_rgt+1 THEN
rgt - ((to_move_rgt - to_move_lft) + 1)
WHEN rgt BETWEEN to_move_lft AND to_move_rgt THEN
rgt + (sibling_rgt - to_move_rgt)
ELSE
rgt
END
END
END
WHERE lft BETWEEN parent_lft+1 AND parent_rgt;
END
|
DELIMITER ;
Maybe that's not the most beautiful piece of code we've ever seen, but it works fine and is probably much more efficient than any kind of sorting algorithm, for example.