Есть несколько алгоритмов хранения деревьев в mysql, и мне кажется, что это в корне неправильно, так как реляционная база данных не предназначена для этого, а представление линейных структур данных в древовидную — один большой костыль в коде и понижение надёжности и скорости приложения. Как я задумался об использование NoSQL в частности mongoDB? Всё просто, меня на собеседование спросили:
Как построить дерево?
Я ответил:
Берём mongodb и документ с деревом из неё
Мне так и не перезвонили, но я не расстраиваюсь за то теперь я изучаю mongoDB и не люблю Mysql(к счастью или к сожалению я не знаю). В общем, разберём как всё это работает? На примере дерева комментариев написанном на php.
Сразу приведу код:
// метод для подключения к mongodb находиться в абстрактном классе
// привожу его для понимания кода ниже
/**
* @return MongoDBDriverManager
* */
static function getConnect() {
if(!is_null(self::$_connect)) {
return self::$_connect;
}
self::$_connect = new MongoDBDriverManager(Config_Db::getConf()['mongodb']['connect']);
return self::$_connect;
}
// наш контроллер
/**
* add comment action
*/
public function addCommentAction()
{
$time = time();
// массив с данными комментария
//insert new comment to the page
$arrData = array(
'page' => $_POST['page_id'], // id страницы в mongo
'time' => $time, // время написания комментария
'name' => $_POST['name'], // имя написавшего
'comment' => $_POST['comment'] // сам комментрарий
);
$connect = Core_Model_Mongo::getConnect(); // подключение к монгодб
// две строки ниже подготовка к записи так как они не относятся к алгоритму
// то в дальнейшем я не буду на них заострять внимание
$write = new MongoDBDriverBulkWrite();
$writeConcern = new MongoDBDriverWriteConcern(MongoDBDriverWriteConcern::MAJORITY);
// и так, если человек отвечает на чей-то комментарий, то к нам приходит reply с id комментария
if(isset($_POST['reply']) && !empty($_POST['reply'])) {
// id комментария
$reply = $_POST['reply'];
// путь для сохранения комментария
// другими словами, это путь по которому будет сохранен комментарий
$path = '';
if(isset($_POST['path']) && !empty($_POST['path'])) {
$path = $_POST['path'];
} else {
$path = 'replies';
}
// собственно, обновление данных по комментарию
$write->update(
array('_id' => new MongoDBBSONObjectID($reply)), // загружаем комментарий
array('$push' => array($path => $arrData) ) // делаем push, в существующие данные, относительно path
);
} else {
$write->insert($arrData); // если комментарий новый вставляем в базу
}
// запускаем запрос
$connect->executeBulkWrite(Config_Db::getConf()['mongodb']['db'] . '.comments', $write, $writeConcern);
// и возвращаемся обратно
header('Location:' . $_POST['back_url']);
}
И так в переменную path передаётся путь для сохранения, к примеру у нас в монго есть комментарий, представим его в виде 'чистого' массива, так как в реальности это массив объектов мы упростим всё до массива.
array (
'name' => 'test',
'comment' => 'test comment',
);
После update, с push и переменной path = 'reply', наш массив приобретёт вид:
array (
'name' => 'test',
'comment' => 'test comment',
'reply' => array (
[0] =>
array(
'name' => 'test reply test'
'comment' => 'test comment reply'
)
)
);
А если отвечать на вложенный комментарий, то путь приобретёт вид 'reply.0.reply', то есть в reply комментария взять 0 элемент, и вставить туда новое поле reply с данными, после операции мы получим следующий массив.
array (
'name' => 'test',
'comment' => 'test comment',
'reply' => array (
[0] =>
array(
'name' => 'test reply test'
'comment' => 'test comment reply'
'reply' => array (
[0] =>
array(
'name' => 'test reply test'
'comment' => 'test comment reply'
)
)
)
)
);
И это всё, весь алгоритм, просто вставить и потом просто взять этот массив для рендера таким какой он был, есть и будет. Рендер не буду расписывать так как рендер это не такая важная часть, да и я просто его делаю через print_r() шутка конечно, но принцип тот же… Спасибо за внимание дорогие хаброюзеры.
П.С.: В примере представлен алгоритм и в нём есть допущения, а именно отсутствие сортировки комментариев и её персистетности.
Автор: lnroma