Evgenii Legotckoi
Evgenii LegotckoiҚыр. 25, 2016, 1:37 Т.Қ.

PostgreSQL жүйесінде материалдандырылған жол

Реляциялық дерекқорларда ағаш құрылымдарында ақпаратты сақтау қосымша шығындары бар тапсырма болып табылады. Мұндай қосымша шығындар сұраныстар санының көбеюі де, деректер құрылымын ұйымдастыруға қызмет ететін ақпарат көлемінің ұлғаюы да болуы мүмкін.

Ағаш құрылымдарын ұйымдастырудың жалпы тәсілдері:

Аты Сипаттама
Көршілестер тізімі Көршілестер тізімі Мәліметтер құрылымын ұйымдастыру әрбір объект негізгі объект туралы ақпаратты сақтайды, яғни кесте жолында осы нысан кірістірілген объектінің идентификаторын көрсететін қосымша өріс бар.
Кірістірілген жиын Кірістірілген жиын Кірістірілген жиындар сол және оң жақ пернелер деп аталатындар туралы ғана емес, сонымен қатар кірістіру деңгейі туралы ақпаратты сақтайды. Деректер құрылымын ұйымдастырудың бұл нұсқасын оқу оңай, бірақ өзгерту қиынырақ.
Материалданған жол Материалдандырылған жол Бұл деректер құрылымының идеясы әрбір жазба ағаштың түбір элементіне толық жолды сақтайды.

Бұл деректер құрылымдарын қарастыру сайттағы мақалаларға түсініктемелерді қолдауды енгізу қажеттілігінен туындады.


Жақындар тізімі маған түсініктемелерді ұйымдастыру үшін біршама ыңғайсыз шешім болып көрінді, өйткені негізгі нысандарды табу және ағаш құрылымын құру кезінде қиындықтар болуы мүмкін, дегенмен тәсілдің өзі элементтерді тез жоюға және қосуға мүмкіндік береді.

Кірістірілген жиын - бұл шағын сайтта пікірлерді ұйымдастырудың өте қиын әдісі, тіпті Habr сияқты үлкен ресурстарға қарасаңыз да, мақалалар астында 5000 пікір жиі бола бермейді. Мақаладағы бірінші бос түсініктеме деп аталатын, бүкіл ағаш салынатын жасырын түбір элементін қосу қажет болатындығы мені де шатастырды. Неліктен қосымша нысандарды шығару керек? - Мен қателескен шығармын. Ал, «қарсы» соңғы дәлел жаңа пікірлерді қосу кезінде кілттерді қайта есептеу қажеттілігі болды. Жалпы, бұл құрылымның артықшылықтарына қарамастан, осы сайттың аясында және оның қазіргі жағдайы - пікірлерді сақтаудың бұл тәсілі торғайларға зеңбірек отына айналады.

Бірақ Материалданған жол бізге қажет нәрсе болып көрінді. Әрбір түсініктемеде толық жол бар. Сонымен қатар мақаланың астында бірнеше түсініктеме ағаштарын ұйымдастыруға болады. Яғни, бірінші деңгейде тұрған кез келген түсініктеме автоматты түрде оның ағашының түбірі болып саналады. Ал жаңа түсініктеме қосқан кезде болашақ ата-анадан толық жолды алып, оған тек жаңа түсініктеменің идентификаторын қосу керек. Сонымен қатар, белгілі бір дерекқорға қатысты және біз PostgreSQL туралы айтып отырмыз, жолды сақтау бүтін мәндердің массиві ретінде ұсынылуы мүмкін, өйткені PostgreSQL массивтерді қолдайды (Айтпақшы, мен көбірек MySQL -ге қарағанда PostgreSQL -мен жұмыс істеу туралы оқыңыз, соғұрлым бұл маған ұнайды). Сондай-ақ, түсініктеме ағаштарын таңдаған кезде осы массивтер бойынша сұрыптауға болады, бұл ағаш құрылымын сақтай отырып, барлық түсініктемелерді хронологиялық тәртіпте автоматты түрде реттеуге мүмкіндік береді, содан кейін [Django] үлгісінде (https://evileg.com) /ru/knowledge/django/ ) бетті қалыпты циклде ешқандай рекурсиясыз жасайды, себебі барлық түсініктемелер дерекқор құралдарымен дұрыс сұрыпталады.

PostgreSQL жүйесінде қолдану

Енді бізде келесі құрылымы бар бірнеше түсініктемелері бар дерексіз бет бар деп елестетіп көрейік:

id | name
-------------------
1  | comment\_1
2  |   comment\_1\_1
3  |     comment\_1\_1\_1
4  |   comment\_1\_2
5  | comment\_2
6  |     comment\_2\_1
7  |     comment\_2\_2
8  | comment\_3

Нәтижесінде түсініктеме құрылымы келесі сұрау арқылы қалыптасады:

CREATE TABLE comments (
  id serial primary key,
  path integer[] not null,
  content varchar(200) not null
);

Бұл құрылымның бірегей автоматты өсетін идентификаторы бар, оны хронологиялық идентификатордың бір түрі ретінде де қарастыруға болады, өйткені теориялық тұрғыдан алғанда, кейінгі уақыты бар түсініктеме дерекқорда төменгі идентификаторы бар түсініктемеден бұрын пайда бола алмайды.

Дерекқорды түсініктемелермен толтырыңыз:

INSERT INTO comments (path, content) VALUES 
  ('{1}', 'comment\_1'),
  ('{1,2}', 'comment\_1\_1'),
  ('{1,2,3}', 'comment\_1\_1\_1'),
  ('{1,4}', 'comment\_1\_2'),
  ('{5}', 'comment\_2'),
  ('{5,6}', 'comment\_2\_1'),
  ('{5,7}', 'comment\_2\_2'),
  ('{8}', 'comment\_3');

Нәтижесінде біз келесі кестені аламыз:

id | path    | content       
-------------------------
1  | {1}     | comment\_1    
2  | {1,2}   | comment\_1\_1  
3  | {1,2,3} | comment\_1\_1\_1
4  | {1,4}   | comment\_1\_2
5  | {5}     | comment\_2  
6  | {5,6}   | comment\_2\_1
7  | {5,7}   | comment\_2\_2
8  | {8}     | comment\_3  

еншілес элементтерді табыңыз

Еншілес элементтерді іздеу үшін массивді қайта анықтау операторын пайдалану керек && . Бұл операторды пайдалана отырып, олардың жолында негізгі элементтің идентификаторы бар элементтерді табамыз. ID = 1 элемент үшін барлық еншілес элементтерді таңдайық.

Сонымен қатар, біз барлық элементтерді ұя салу реті бойынша сұрыптай аламыз, бұл жолдың ұзындығы.

Төмендегі сұрауда :id = 1 .

SELECT * FROM comments
WHERE path && ARRAY[:id]
ORDER BY array\_length(path, 1);

Шығару кезінде біз келесі нәтижені аламыз:

 id |  path   |    content    
----+---------+---------------
  1 | {1}     | comment\_1
  2 | {1,2}   | comment\_1\_1
  4 | {1,4}   | comment\_1\_2
  3 | {1,2,3} | comment\_1\_1\_1
(4 rows)

Негізгі элементтерді табыңыз

Барлық негізгі элементтердің тізімін алу ағымдағы элемент жолының бөлігі болып табылатын барлық элементтерді таңдау болып табылады. Мысалы, біз ID = 3 , яғни түсініктеме_1_1_1. бар түсініктеменің негізгі элементтерінің тізімін аламыз.

Төмендегі сұрауда :id = 3.

SELECT * FROM comments
WHERE id != :id 
AND array[id] && (
  SELECT path 
  FROM comments 
  WHERE id = :id
)
ORDER BY path;

Нәтижесінде біз келесі нәтижені аламыз:

 id | path  |   content   
----+-------+-------------
  1 | {1}   | comment\_1
  2 | {1,2} | comment\_1\_1
(2 rows)

Элементтерді қосу

Жаңа жазбаны жасаған кезде, біз жаңа элементтің идентификаторы қосылатын негізгі элементтің ағымдағы жолы бойымен жолды қалыптастыруымыз керек. Негізгі элемент идентификаторы 4 болатын жаңа жазба қостық делік.

Келесі сұрауда :id = 4.

INSERT INTO comments (content, path)
values (
  'comment\_1\_2\_3', 
  (SELECT path FROM comments WHERE id = :id) || (select currval('comments\_id\_seq')::integer)
);

|| операторы негізгі элементтің жолын жаңа элементтің идентификаторымен біріктіреді. Бұл ретте кестені құру кезінде автоматты түрде жасалатын comments_id_seq тізбегінен жаңа ID мәнін алу үшін біз типті трансляциялауды орындауымыз керек, өйткені жолда Integer типті массив бар. [] , және currval biginteger түрін қайтарады.

Біз келесі кестені аламыз:

 id |  path   |    content    
----+---------+---------------
  1 | {1}     | comment\_1
  2 | {1,2}   | comment\_1\_1
  3 | {1,2,3} | comment\_1\_1\_1
  4 | {1,4}   | comment\_1\_2
  5 | {5}     | comment\_2
  6 | {5,6}   | comment\_2\_1
  7 | {5,7}   | comment\_2\_2
  8 | {8}     | comment\_3
  9 | {1,4,9} | comment\_1\_2\_3
(9 rows)

Элементті жою

Элементті жою барлық еншілес элементтерді жою арқылы орындалады, ол сонымен қатар массивтердің қиылысуы арқылы жүзеге асырылады.

Мысалы, идентификаторы 5-ке тең элементті алып тастаймыз. ( :id = 5 )

DELETE FROM comments
WHERE path && ARRAY[:id];

Нәтижесінде кесте келесідей болады:

 id |  path   |    content    
----+---------+---------------
  1 | {1}     | comment\_1
  2 | {1,2}   | comment\_1\_1
  3 | {1,2,3} | comment\_1\_1\_1
  4 | {1,4}   | comment\_1\_2
  8 | {8}     | comment\_3
  9 | {1,4,9} | comment\_1\_2\_3
(6 rows)

Элементті жылжыту

Соңында элементтердің бірін оның ұрпақтарымен бірге басқа элементке жылжытайық. Таңдау үшін көп нәрсе болмағандықтан, ID = 2 элементті ID 8-ге тең элементке жылжытамыз.

Жалпы, бір элементті екіншісіне жылжыту үшін осы элемент пен оның барлық еншілестері үшін ағымдағы ата-анаға сәйкес келетін жол бөлігін жаңа ата-анаға сәйкес келетін жолға ауыстыру қажет.

Жалғыз қиындық - элемент деңгейін алу үшін бізге жылжытуды бір сұранысқа айналдыруға көмектесетін сақталған процедураны жазу керек. Оны деңгей деп атаймыз және оған біз жылжытатын элементтің идентификаторын береміз.

CREATE OR REPLACE FUNCTION level(element\_id integer) RETURNS integer AS
$$
DECLARE result integer;
BEGIN
SELECT array\_length(path, 1) FROM comments WHERE comments.id = element\_id INTO result;
RETURN result;
END
$$ LANGUAGE plpgsql;

Сақталған процедура тіркелгеннен кейін біз оны деректерді тасымалдау сұрауында қолданамыз.

Бұл сұрау мыналарды қамтиды:

  • :new_parent_id - болашақ ата-ананың бірегей идентификаторы, (8-ге тең);
  • :жылжымалы_id – жылжытылатын элементтің бірегей идентификаторы (2-ге тең).
UPDATE comments
SET path = (
  SELECT path
  FROM comments
  WHERE id = :new\_parent\_id
) || path[(SELECT level(:movable\_id)) : array\_length(path, 1)]
WHERE path && ARRAY[:movable\_id];

Нәтижесінде біз келесі пішіндегі кестені аламыз:

 id |  path   |    content    
----+---------+---------------
  1 | {1}     | comment\_1
  4 | {1,4}   | comment\_1\_2
  8 | {8}     | comment\_3
  9 | {1,4,9} | comment\_1\_2\_3
  2 | {8,2}   | comment\_1\_1
  3 | {8,2,3} | comment\_1\_1\_1
(6 rows)
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

Mitai .
  • Қаң. 5, 2020, 3:54 Т.Ж.

простите а Django MPTT в этом варианте не подходит? она вроде как раз для этих целей разрабатывалась

Evgenii Legotckoi
  • Қаң. 6, 2020, 3:11 Т.Ж.

Добрый день. Почитал описание Django MPTT, и да, подойдёт. Рассматривайте статью как базовую теорию.

Nomad
  • Қар. 2, 2020, 11:02 Т.Ж.
  • (өңделген)

Django MPTT использует для построения дерева алгоритм Nested Set

во вторых если надо создавать динамическое дерево прямо в хтмл документе, Django MPTT, не подойдет, а вернее подойдет только если вы знакомы с алгоритмом Nested Set, а это означает что при добавлении нового элемента в дерево надо перешитать полностью все значения записей по полям lft и rght имея в виду еще поля parent_id и tree_id

=====

вопрос к автору статьи, скажите а Materialized Path сохраняет ордер элементов дерева. предположим что у нас есть дерево:

comment_1
comment_1_1
comment_1_1_1
comment_1_2
comment_2
comment_2_1
comment_2_2
comment_3

мы тут видим 3 родителя: comment_1, comment_2, comment_3

и мы видим что у родителя comment_2 есть 2 чаилда: comment_2_1 и comment_2_2

вот если мы поменяем местами данные чаилды как Materialized Path сохранит это????

Evgenii Legotckoi
  • Қаң. 28, 2021, 4:06 Т.Ж.

Самый простой способ, который приходит на ум, это скопировать comment_2_1 до нового комментария comment_2_3, чтобы у него был более старший id и переписать колонку path. А оригинальный comment_2_1 удалить. Только так.

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
OI
  • Ora Iro
  • Жел. 24, 2024, 6:38 Т.Ж.

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:40ұпай,
  • Бағалау ұпайлары-8
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:50ұпай,
  • Бағалау ұпайлары-4
m
  • molni99
  • Қаз. 26, 2024, 1:37 Т.Ж.

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:80ұпай,
  • Бағалау ұпайлары4
Соңғы пікірлер
ИМ
Игорь МаксимовҚар. 22, 2024, 11:51 Т.Ж.
Django - Оқулық 017. Теңшелген Django кіру беті Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiҚаз. 31, 2024, 2:37 Т.Қ.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEҚаз. 19, 2024, 8:19 Т.Ж.
Qt Creator көмегімен fb3 файл оқу құралы Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовҚаз. 5, 2024, 7:51 Т.Ж.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Қар. 15, 2024, 6:04 Т.Ж.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectМаусым 4, 2022, 3:49 Т.Ж.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimҚаз. 25, 2024, 9:10 Т.Ж.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Бізді әлеуметтік желілерде бақылаңыз