Проект "Свободные голосования"
http://gplvote.andyhost.ru/forum/

Полезные статьи - для разработчиков
http://gplvote.andyhost.ru/forum/viewtopic.php?f=7&t=91
Страница 1 из 2

Автор:  Андрей [ 07 авг 2011, 14:50 ]
Заголовок сообщения:  Полезные статьи - для разработчиков

Оригинал - http://doc.aceweb.ru/full_9_200_modered.html

Хранение древовидных структур в Базах данных (Nested Sets)

Думаю с проблемой хранения деревьев в MySQL сталкивались многие и мне не нужно объяснять сколько при этом возникает проблем. В данной статье я хочу рассказать об одном из методов хранения деревьев под названием Вложенные множества (Nested Sets). Для начала описание метода. Взгляните на рисунок ниже (рисую я плохо ):

Изображение

На нем представлено дерево, описанное по всем правилам метода Вложенных множеств.
Квадратами обозначены узлы дерева, красные цифры в центре узлов - просто уникальные идентификаторы узла, а цифры в углах - это левое и правое смещения. Именно в этих двух цифрах - левом и правом смещении заложена вся информация о дереве. И если информацию о смещениях занести в базу данных, то работа с деревом намного упрощается.
Обратите внимание на то, в каком порядке проставлены эти смещения. Если мысленно пройтись по порядку от 1 до 22, то вы обойдете все узлы дерева слева направо. Фактически это путь обхода всех узлов дерева слева направо. Опишу правила по которым эти смещения расставляются:

* Начинать обход дерева нужно с корневого узла и в нем же заканчивать.
* При самом первом входе в узел нужно оставить цифру в его левом углу и при последнем выходе из узла нужно оставить цифру в правом углу.
* Все цифры расставляются, начиная с 1.
* Последняя цифра должна быть в 2 раза больше количества узлов дерева (потому что в каждом узле оставляем по 2 по указанным выше правилам)

Данная статья предназначена прежде всего для PHP-программистов. Поэтому я не буду вдаваться в подробности как вручную организовать такое дерево в mysql-таблице, поскольку для этого есть готовый класс - CDBTree. Скачать его можно с http://dev.e-taller.net/dbtree/

На этом теорию заканчивается и начинается практика. Предположим нужно сделать каталог ресурсов с категориями неограниченой вложенности. При этом перед программистом возникает несколько микро-задач:

* Нужно сделать вывод дерева категорий
* Нужно получить список ВСЕХ подкатегорий для указанной категории
* Нужно получить список подкатегорий, уровень вложенности которых на единицу больше уровня вложенности указанной категории
* Нужно получить список всех родительских категорий для указанной категории (для посторения пути к текущей категории)

Замечу, что класс CDBTree еще хранит в таблице уровень вложенности для данного узла. Это факт облегчит нам решение задачи номер 3 Для начала создайте таблицу:

Код:
CREATE TABLE categories (
cid int(10) unsigned NOT NULL auto_increment,
title varchar(128) NOT NULL default ,
cleft int(10) unsigned NOT NULL default 0,
cright int(10) unsigned NOT NULL default 0,
clevel int(10) unsigned NOT NULL default 0,
PRIMARY KEY (cid),
KEY cleft (cleft, cright, clevel)
) TYPE=MyISAM;


Данная таблица представлена только для примеров. В конце статьи я приведу слова автора класса по поводу организации таблиц для хранения деревьев. А теперь выполните следующий скрипт:

Код:
<?
/*
   Работа с деревом по алгоритму Nested Sets.
   Подготовка таблицы к работе.
   Используется класс dbtree.php
   Взять его можно: http://dev.e-taller.net/dbtree/

    ---------------------
    Author: Maxim Matykhin.
    mailto: max@webscript.ru
*/

$table=categories; // таблица категорий
$id_name=cid;     // имя поля первичного ключа
$field_names = array( // имена полей таблицы
   left => cleft,
   right=> cright,
   level=> clevel,
);

require_once cd.php;
require_once dbtree.php;

$dbh=new CDataBase(dbname, localhost, root, password);
$Tree = new CDBTree($dbh, $table, $id_name, $field_names);
// создаем корневую запись (см. пояснения к статье)
$id=$Tree->clear(array(title=>Каталог ресурсов));

$level_2=array();
$level_2[0]=$Tree->insert($id,array(title=>Программирование));
$level_2[1]=$Tree->insert($id,array(title=>Новости));
$level_2[2]=$Tree->insert($id,array(title=>Сопрт));
$level_2[3]=$Tree->insert($id,array(title=>Разное));

// теперь создадим несколько записей третьего уровня
$level_3=array();
$level_3[0]=$Tree->insert($level_2[0],array(title=>PHP));
$level_3[1]=$Tree->insert($level_2[0],array(title=>Perl));
$level_3[2]=$Tree->insert($level_2[0],array(title=>Delphi));

$level_3[3]=$Tree->insert($level_2[1],array(title=>Криминал));

$level_3[4]=$Tree->insert($level_2[2],array(title=>Футбол));
$level_3[5]=$Tree->insert($level_2[2],array(title=>Шахматы));

$level_3[6]=$Tree->insert($level_2[3],array(title=>Медицина));
$level_3[7]=$Tree->insert($level_2[3],array(title=>Экология));
$level_3[8]=$Tree->insert($level_2[3],array(title=>Промышленность));
                                             
// и для некоторых сделаем четвертый уровень
$Tree->insert($level_3[0],array(title=>PEAR));
$Tree->insert($level_3[8],array(title=>Металлургия));
$Tree->insert($level_3[6],array(title=>Морги));
echo Таблица заполнена.;
?>


Данный пример просто заполняет таблицу (просто чтобы следующие примеры потестить на ней).
Думаю код здесь ясен. Наверное стоит лишь объяснить строку:

Код:
<? $id=$Tree->clear(array(title=>Каталог ресурсов)); ?>


Данная строка вставляет корневой узел в таблицу. Корневой узел должен быть один. Также следует заметить, что этот метод очищает таблицу, перед вставкой, так что будьте осторожны.
Теперь давайте выведем дерево. Для этого достаточно просто вывести все элементы, отсортировав их по cleft:

Код:
<?
$query=SELECT * FROM .$table. ORDER BY cleft ASC;
$result=$dbh->query($query);
while($row = $dbh->fetch_array($result))
{
   echo str_repeat(&nbsp;,6*$row[clevel]).$row[title].<br>;
}
?>


В данном случае для расчета величины отступа используется значение clevel. Теперь посмотрим, как получить все подкатегории. Это можно сделать либо с помощью метода enumChildrenAll($cid); либо написав правильный SQL-запрос. Предположим есть категория параметры которой $cleft, $cright и $clevel. Тогда запрос, получающий все дочерние узлы, выглядит так:

Код:
SELECT
cid,
title,
clevel
FROM
categories
WHERE
cleft BETWEEN $cleft AND $cright
ORDER BY cleft


Выполнив этот запрос, вы получите все подкатегории для указанной категории Метод enumChildrenAll($cid); также возвращает подкатегории, но он возвращает только их идентификаторы, и нет встроенных методов для того чтобы он возвращал дополнительные поля (конечно же вы можете внести изменения в класс).

Для получения списка подкатегорий, уровень вложенности которых на единицу больше уровня вложенности указанной категории происходит.

Код:
SELECT
cid,
title,
clevel
FROM
categories
WHERE
cleft BETWEEN $cleft AND $cright AND
clevel = $clevel+1
ORDER BY cleft


И последнее - определение родительских категорий.
Запрос должен выглядеть так:

Код:
SELECT
cid,
title,
clevel
FROM
categories
WHERE
cleft < $cleft AND
cright > $cright
ORDER BY cleft


В классе CDBTree для этого есть метод enumPath($cid).
Теперь об рекомендациях автора класса:

опыт показывает, что структуру с обходом дерева лучше хранить отдельно от
данных, т.к. в этом случае при обновлении таблицы очень долго обновляются
индексы, да и данные могут сделать невозможным формат записи фиксированной
длины, что тоже кардинально скажется на скорости.
Самой оптимальной структурой по-моему будет:

Код:
CREATE TABLE categories (
cat_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
cat_left INT UNSIGNED NOT NULL,
cat_right INT UNSIGNED NOT NULL,
cat_level INT UNSIGNED NOT NULL,
PRIMARY KEY(cat_id)
KEY (cat_left, cat_right, cat_level)
);


В итоге MySQL за многими запросами даже не будет обращаться к файлу с данными.
Ему будет хватать файла с индексами. А когда уже пройдёт всё фильтрование в
таблице с деревом, по примари-кею быстро произойдёт линкование с остальными
данными.

Также добавлю, что автор класса рекомендует использовать его (класс) только для добавления/удаления узлов в дерево, а для получения узлов писать SELECT-запросы самому.

Описание методов класса CDBTree

CDBTree(&$DB, $tableName, $itemId, $fieldNames=array())
конструктор
Параметры:

* &$DB - ссылка на объект класса CDatabase (этот класс лежит в архиве с dbtree.php);
* $tableName - имя таблицы, в которой лежит "дерево";
* $itemID - имя первичного ключа таблицы в которой лежит "дерево";
* $fieldNames - массив с именами полей таблицы

function getElementInfo($ID)
Возвращает массив с информацией о об элементе с указанным $ID (параметры left, right, level).
function clear($data=array())
данная функция очищает таблицу и вставляет в нее корневой узел дерева.
В массиве $data должна быть информация в виде array("db_field"=>"value", ...)
Поля left, right и level будут вставлены автоматически.
function update($ID, $data)
этот метод используется для изменения информации об указанном элементе. Параметры:

* $ID - идентификатор узла
* $data - массив с обновленными данными. (параметры left, right и level вставлять не нужно)

function insert($ID, $data)
Метод для вставки узла в дерево. Параметры:

* $ID - идентификатор родительского узла
* $data - массив с обновленными данными. (параметры left, right и level вставлять не нужно)

function delete($ID)
Удаляет указанный узел не удаляя его "потомков";
function deleteAll($ID)
Удаляет указанный узел вместе с его "потомков";
function enumChildrenAll($ID)
Определяет всех "потомков" для указанного узла
function enumChildren($ID, $start_level=1, $end_level=1)
Определяет потомков для указанного узла

* $start_level - начальный уровень вложенности узла с которого нужно искать "потомков";
* $end_level - конечный уровень вложенности узла до которого нужно искать "потомков";
Если $end_level не указан, (равен 0) то ищутся все узлы "глубже" $start_level

function enumPath($ID, $showRoot=false)
Определяет всех родителей для указанного узла.

* $showRoot - true, если нужно выводить корневой элемент.

Последние три описанные функции формируют SQL-запрос таким образом, что выводятся только идентификаторы узла.

Остальные методы мне использовать не приходилось. Поэтому описывать их не буду. В последней версии класса появился метод MoveAll для перемещения узлов в дереве, но пока он работает с багами.

Автор:  yurial [ 03 окт 2011, 00:25 ]
Заголовок сообщения:  Re: Полезные статьи - для разработчиков

Код:
OpenPGP Message Format

Status of this Memo

   This document specifies an Internet standards track protocol for the
   Internet community, and requests discussion and suggestions for
   improvements.  Please refer to the current edition of the "Internet
   Official Protocol Standards" (STD 1) for the standardization state
   and status of this protocol.  Distribution of this memo is unlimited.

Copyright Notice

   Copyright (C) The Internet Society (1998).  All Rights Reserved.


http://www.cryptnet.net/mirrors/rfcs/rfc2440.txt

Автор:  yurial [ 03 окт 2011, 00:30 ]
Заголовок сообщения:  Re: Полезные статьи - для разработчиков

Код:
Session Traversal Utilities for NAT (STUN)

Status of This Memo

   This document specifies an Internet standards track protocol for the
   Internet community, and requests discussion and suggestions for
   improvements.  Please refer to the current edition of the "Internet
   Official Protocol Standards" (STD 1) for the standardization state
   and status of this protocol.  Distribution of this memo is unlimited.

http://tools.ietf.org/html/rfc5389
http://www.rfc-editor.org/rfc/rfc5389.txt

Автор:  yurial [ 03 окт 2011, 01:28 ]
Заголовок сообщения:  Re: Полезные статьи - для разработчиков

Схема разделения секрета Шамира

Автор:  yurial [ 08 окт 2011, 14:48 ]
Заголовок сообщения:  Re: Полезные статьи - для разработчиков

Код:
Explicit Multicast (Xcast) Concepts and Options

Status of This Memo

   This memo defines an Experimental Protocol for the Internet
   community.  It does not specify an Internet standard of any kind.
   Discussion and suggestions for improvement are requested.
   Distribution of this memo is unlimited.

http://tools.ietf.org/html/rfc5058

Автор:  yurial [ 23 окт 2011, 20:18 ]
Заголовок сообщения:  Re: Полезные статьи - для разработчиков

кривость UUID генератора в OS Windows

Автор:  Андрей [ 24 окт 2011, 17:02 ]
Заголовок сообщения:  Re: Полезные статьи - для разработчиков

Особенности генераторов случайных чисел на смарт-картах и их уязвимость

Автор:  yurial [ 14 дек 2011, 18:06 ]
Заголовок сообщения:  Re: Полезные статьи - для разработчиков

std::vector vs std::map

Автор:  yurial [ 14 дек 2011, 18:08 ]
Заголовок сообщения:  Re: Полезные статьи - для разработчиков

van Emde Boas tree

Автор:  evorios [ 05 фев 2012, 16:11 ]
Заголовок сообщения:  Re: Полезные статьи - для разработчиков

https://bitcointalk.org/index.php?topic=61486.msg731946#msg731946 писал(а):
scrypt-функция скопипащена из проекта, автор которого разработал ее для хэширования паролей произвольной длины и написал о ней работу с математическим описанием. Да и вообще, статья очень интересная, там есть к примеру оценка стоимости оборудования для взлома паролей, зашифрованных разными функциями.

http://www.tarsnap.com/scrypt/scrypt.pdf


Изображение

Страница 1 из 2 Часовой пояс: UTC + 3 часа [ Летнее время ]
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/