ВыходВход

Меню сайта

Категории статей
Теория [1]
Древовидные структуры данных. Методология, описание
Использование в SQL DB [0]
Решения используемые в SQL DB
Использование в Perl [7]
Применение технологии Nested Sets в Perl программировании
Использование в PHP [2]
Применение технологии Nested Sets в PHP программировании

Меню пользователя

Поиск по статьям

Друзья сайта

Модуль Perl - 2. Перемещение (часть первая)
» Каталог статей » Использование в Perl
Модуль Perl - 2. Перемещение (часть первая)

Модуль Perl - 2. Перемещение (часть первая)

4. Перемещение узла

Любое перемещение узла производится с помощью одних и тех же запросов, и требуют одних и тех же данных, поэтому правильней выделить данное действие в отдельный блок (процедуру). Для перемещения нам нужно взять следующие данные:

  • данные перемещаемого узла;
  • точка перемещения, куда перемещается узел

И произвести следующие действия:

  • определить диапазоны смещения для ключей дерева и перемещаемого узла;
  • определить вид перемещения: "вверх " - "вниз" по дереву;
  • собственно, переместить узел и обновить ключи дерева
sub _move_unit {
# Получаем данные: объект и данные для перемещения 
    my ($self, $data) = @_;
# Проверяем возможность перемещения* 
    if ($data->{'near'} >= $data->{'left'} && $data->{'near'} <= $data->{'right'}) {return 0}
# Определяем смещение ключей перемещаемого узла и смещение уровня 
    my $skew_tree = $data->{'right'} - $data->{'left'} + 1;
    my $skew_level = $data->{'level_new'} - $data->{'level'};
# Если перемещаем вверх по дереву
    if ($data->{'right'} < $data->{'near'}) {
# Определяем смещение ключей для дерева 
        my $skew_edit = $data->{'near'} - $data->{'left'} + 1 - $skew_tree;
# Переносим узел и одновременно обновляем дерево
        $self->{'DBI'}->do('UPDATE '.$self->{'table'}.
                           ' SET '.
                            $self->{'left'}.' = CASE WHEN '.$self->{'right'}.' <= '.$data->{'right'}.' THEN '.
                             $self->{'left'}.' + '.$skew_edit.' ELSE CASE WHEN '.$self->{'left'}.' > '.$data->{'right'}.' THEN '.
                              $self->{'left'}.' - '.$skew_tree.' ELSE '.$self->{'left'}.' END END, '.
                            $self->{'level'}.' = CASE WHEN '.$self->{'right'}.' <= '.$data->{'right'}.' THEN '.
                             $self->{'level'}.' + '.$skew_level.' ELSE '.$self->{'level'}.' END, '.
                            $self->{'right'}.' = CASE WHEN '.$self->{'right'}.' <= '.$data->{'right'}.' THEN '.
                             $self->{'right'}.' + '.$skew_edit.' ELSE CASE WHEN '.$self->{'right'}.' <= '.$data->{'near'}.' THEN '.
                              $self->{'right'}.' - '.$skew_tree.' ELSE '.$self->{'right'}.' END END WHERE '.
                            $self->{'right'}.' > '.$data->{'left'}.' AND '.
                            $self->{'left'}.' <= '.$data->{'near'}.
                            ($self->{'type'} eq 'M' ? ' AND '.$self->{'multi'}.'= \''.$data->{'multi'}.'\'' : '') 
                          );
# Если перемещаем вниз по дереву
    } else {
# Определяем смещение ключей для дерева 
        my $skew_edit = $data->{'near'} - $data->{'left'} + 1;
# Переносим узел и одновременно обновляем дерево
        $self->{'DBI'}->do('UPDATE '.$self->{'table'}.
                           ' SET '.
                            $self->{'right'}.' = CASE WHEN '.$self->{'left'}.' >= '.$data->{'left'}.' THEN '.
                             $self->{'right'}.' + '.$skew_edit.' ELSE CASE WHEN '.$self->{'right'}.' < '.$data->{'left'}.' THEN '.
                              $self->{'right'}.' + '.$skew_tree.' ELSE '.$self->{'right'}.' END END, '.
                            $self->{'level'}.' = CASE WHEN '.$self->{'left'}.' >= '.$data->{'left'}.' THEN '.
                             $self->{'level'}.' + '.$skew_level.' ELSE '.$self->{'level'}.' END, '.
                            $self->{'left'}.' = CASE WHEN '.$self->{'left'}.' >= '.$data->{'left'}.' THEN '.
                             $self->{'left'}.' + '.$skew_edit.' ELSE CASE WHEN '.$self->{'left'}.' > '.$data->{'near'}.' THEN '.
                              $self->{'left'}.' + '.$skew_tree.' ELSE '.$self->{'left'}.' END END WHERE '.
                            $self->{'right'}.' > '.$data->{'near'}.' AND '.
                            $self->{'left'}.' < '.$data->{'right'}.
                            ($self->{'type'} eq 'M' ? ' AND '.$self->{'multi'}.'= \''.$data->{'multi'}.'\'' : '')
                      );
    }
    return 1
}

* Примечание: Мы не можем переместить узел "в себя" поэтому сделали соответсвующую проверку

Где:

  • $data->{'left'} - левый ключ перемещаемого узла;
  • $data->{'right'} - правый ключ перемещаемого узла;
  • $data->{'level'} - уровень перемещаемого узла;
  • $data->{'multi'} - идентификатор дерева перемещаемого узла (если мультидеревья);
  • $data->{'level_new'} - уровень - куда перемещается узел;
  • $data->{'near'} - "точка перемещения" будущий левый ключ перемещаемого узла, уменьшенный на единицу;

Имя процедуры начинается, с "_" и это не случайно, так как этот метод не будет вызываться из скрипта, а использоваться как внутренняя процедура модуля.

В итоге, чтобы переместить узел, нам требуется сформировать переменную $data (ссылка на хеш) и передать её в процедуру _move_unit.

4.1. Перемещение узла в подчинение другому

Для того, что бы переместить узел в подчинение другому, нам нужно взять следующие данные:

  • параметры перемещаемого узла;
  • параметры родительского узла (куда перемещаем) ;
  • параметр перемещения узла - в начало или конец подчиненного списка перемещать.

И произвести следующие действия:

  • определить параметры перемещаемого узла (если они еще не определены);
  • определить параметры родительского узла;
  • передать полученные данные процедуре _move_unit
sub set_unit_under {
# Получаем данные: объект, перемещаемый узел, место перемещения, порядок перемещения 
    my ($self, %common)= @_;
# перемещаемый узел
    my $unit = $common{'unit'} || undef;
# место перемещения
    my $under = $common{'under'} || undef;
#  порядок перемещения (top - в начало, иначе - в конец списка) 
    my $order = $common{'order'} || undef;
# объявляем переменную, которую будем передавать процедуре перемещения
    my $data;
# определяем данные перемещаемого узла
    if ($unit) {$self = &select_unit($self, $unit)}
    elsif (!$self->{'unit'}->{'id'}) {croak("NestedSets failed: Your must first select unit, for moving it!!!")}
# если место перемещения - корень дерева 
    if (!$under || $under eq 'none' || $under eq 'root') {
# если порядок перемещения - начало списка 
        if (($order && $order eq 'top') || $self->{'order'} eq 'T') {
            $data->{'near'} = 0;
            $data->{'level_new'} = 1
        } else {
# иначе выбираем максимальное значение ключа дерева
            my $sql = 'SELECT MAX('.$self->{'right'}.') AS num FROM '.$self->{'table'}.
                      ($self->{'type'} eq 'M' ? 
                       ' WHERE '.$self->{'multi'}.'='.$self->{'unit'}->{'multi'} : '');
            my $sth = $self->{'DBI'}->prepare($sql); $sth->execute();
            my $row = $sth->fetchrow_hashref(); 
            $sth->finish();
            if ($row) {$data->{'near'} = $$row{'num'}; $data->{'level_new'} = 1}
            else {croak("NestedSets failed: The place of moving is not determined, check up his!!!")}
        }
# иначе получаем данные места перемещения 
    } else {
        my $sql = 'SELECT '.
                 $self->{'left'}.' AS lk, '.
                 $self->{'right'}.' AS rk, '.
                 $self->{'level'}.' AS lv FROM '.$self->{'table'}.
               ' WHERE '.$self->{'id'}.' = \''.$under.'\''.
                ($self->{'type'} eq 'M' ? 
                  ' AND '.$self->{'multi'}.'= \''.$self->{'unit'}->{'multi'}.'\'' : '');
        my $sth = $self->{'DBI'}->prepare($sql); $sth->execute();
        my $row = $sth->fetchrow_hashref(); $sth->finish();
# в зависимости от порядка перемещения берем либо правый, либо левый ключ 
        if ($row && (($order && $order eq 'top') || $self->{'order'} eq 'T')) {
            $data->{'near'} = $$row{'lk'};
            $data->{'level_new'} = $$row{'lv'} + 1 
        } elsif ($row) {
            $data->{'near'} = $$row{'rk'} - 1; 
            $data->{'level_new'} = $$row{'lv'} + 1
        } else {croak("NestedSets failed: The place of moving is not determined, check up his!!!")} 
    }
# перебрасываем из объекта данные о перемещаемом узле
    $data->{'left'} = $self->{'unit'}->{'left'};
    $data->{'right'} = $self->{'unit'}->{'right'};
    $data->{'level'} = $self->{'unit'}->{'level'};
    $data->{'multi'} = $self->{'unit'}->{'multi'} || undef;
    $self->{'unit'} = undef; 
# перемещаем узел 
    &_move_unit($self, $data);
    return 1 
}

Вызов данного метода производится так:

...
    my $unit = ... # Определяем идентификатор узла 
    my $under = ... # Определяем нового родителя      
... 
use MyModule::NestedSets;
my $nested = new MyModule::NestedSets {table=>'catalog_category', type=>'M', DBI=>$dbh};
$nested->set_unit_under(unit=>$unit, under=>$under, order=>'top');
... 

или так:

...
    my $unit = ... # Определяем идентификатор узла 
    my $under = ... # Определяем нового родителя      
... 
use MyModule::NestedSets;
my $nested = new MyModule::NestedSets {table=>'catalog_category', type=>'M', DBI=>$dbh};
$nested->select_unit($unit);
$nested->set_unit_under(under=>$under, order=>'top');
... 

4.2. Перемещение узла - рядом с другим

Для того, что бы переместить узел в подчинение другому, нам нужно взять следующие данные:

  • параметры перемещаемого узла;
  • параметры узла рядом с каким собираемся размещать;
  • параметр перемещения узла - до или после узла переместить.

И произвести следующие действия:

  • определить параметры перемещаемого узла (если они еще не определены);
  • определить рядом с каким собираемся размещать;
  • передать полученные данные процедуре _move_unit
sub set_unit_near {
# Получаем данные: объект, перемещаемый узел, место перемещения, порядок перемещения 
    my ($self, %common)= @_;
# перемещаемый узел
    my $unit = $common{'unit'} || undef;
# место перемещения
    my $near = $common{'near'} || undef;
# порядок перемещения (top - в начало, иначе - в конец списка) 
    my $order = $common{'order'} || undef;
# объявляем переменную, которую будем передавать процедуре перемещения
    my $data;
# определяем данные перемещаемого узла
    if ($unit) {$self = &select_unit($self, $unit)}
    elsif (!$self->{'unit'}->{'id'}) {croak("NestedSets failed: Your must first select unit, for moving it!!!")}
# определяем данные места перемещения - узла, рядом с которым 
# будет располагаться перемещаемый узел
    my $sql = 'SELECT '.
                  $self->{'left'}.' AS lk, '.
                  $self->{'right'}.' AS rk, '.
                  $self->{'level'}.' AS lv FROM '.$self->{'table'}.
              ' WHERE '.$self->{'id'}.' = \''.$near.'\''.
                  ($self->{'type'} eq 'M' ?
                   ' AND '.$self->{'multi'}.'= \''.$self->{'unit'}->{'multi'}.'\'' : '');
    my $sth = $self->{'DBI'}->prepare($sql); $sth->execute(); 
    my $row = $sth->fetchrow_hashref(); 
    $sth->finish();
# в зависимости от порядка перемещения берем либо правый, либо левый ключ
    if ($row && $order && $order eq 'before') {
        $data->{'near'} = $$row{'lk'} - 1; 
        $data->{'level_new'} = $$row{'lv'}
    } elsif ($row) {
        $data->{'near'} = $$row{'rk'};
        $data->{'level_new'} = $$row{'lv'}
    } else {croak("NestedSets failed: The place of moving is not determined, check up his!!!")} 
# перебрасываем из объекта данные о перемещаемом узле
    $data->{'left'} = $self->{'unit'}->{'left'};
    $data->{'right'} = $self->{'unit'}->{'right'};
    $data->{'level'} = $self->{'unit'}->{'level'};
    $data->{'multi'} = $self->{'unit'}->{'multi'} || undef;
    $self->{'unit'} = undef; 
# перемещаем узел 
    &_move_unit($self, $data);
    return 1 
}

Вызов данного метода производится так:

...
    my $unit = ... # Определяем идентификатор узла 
    my $near = ... # Определяем место (узел) перемещения 
... 
use MyModule::NestedSets;
my $nested = new MyModule::NestedSets {table=>'catalog_category', type=>'M', DBI=>$dbh};
$nested->set_unit_near(unit=>$unit, near=>$near, order=>'before');
... 

или так:

...
    my $unit = ... # Определяем идентификатор узла 
    my $near = ... # Определяем место (узел) перемещения
... 
use MyModule::NestedSets;
my $nested = new MyModule::NestedSets {table=>'catalog_category', type=>'M', DBI=>$dbh};
$nested->select_unit($unit);
$nested->set_unit_near(near=>$near, order=>'before');
... 
Категория: Использование в Perl | Добавил: phoinix (2005-10-31) | Автор: Сергей Томулевич (aka Phoinix)
Просмотров: 2428 | Рейтинг: 4.0 |

Комментарии

 

Бесплатный конструктор сайтов - uCoz