Дилемма 5.Узлы или ребра?
DOM представляет собой модель, которая в качестве сущностей использует узлы. Этот метод хорош для хранения документа в виде объектов в памяти, но может стать значительной проблемой при хранении иерархических данных в таблицах SQL. Для хранения, напротив, более подходящим будет метод представления, в котором сущностью являются связи между узлами. Дополнительным условием является наличие у всех узлов однозначных идентификаторов, так чтобы было возможно идентифицировать два одинаковых сестринских узла. Поскольку все связи возможно представить в виде простой таблицы, то реберное представление позволяет просто (с учетом наличия идентификаторов) хранить XML в SQL базе данных с максимальной степенью детализации.
Рассмотрим простой пример, иллюстрирующий сказанное. Допустим, у нас есть простая XML-схема (модель статьи):
Рисунок: Модель статьи
На основе этой модели можно создать такой валидный документ:
<?xml version="1.0" encoding="UTF-8"?> <article codename="XML"> <head> <filename>Text</filename> <author>Vasia Pupkin</author> <email>ya_vasia@bigmir.net</email> <editor>ChebotarjovA.</editor> <chars>456</chars> </head> <body> <title>XML parsing</title> <annotation>It's just an example.</annotation> <data> <par> Body text of article. </par> </data> </body> </article> |
Добавим к каждому элементу идентификатор для введения определенности в случаях, когда есть последовательность однотипных элементов (вообразите, что у вас есть отношение «первый параграф раздела», но сам-то раздел должен быть идентифицирован). Поскольку у текста и комментариев не может быть дочерних узлов, то для них можно не вводить идентификаторов. Можно ввести культуру (правило) присвоения имен узлам, можно их просто вынимать из «черного ящика» — главное, что они должны быть уникальными. Для автоматизации такой операции нам, очевидно, пригодится SAX. Условно такой документ может выглядеть так (на самом деле его нет необходимости явно генерировать):
<?xml version="1.0" encoding="UTF-8"?> <article ID="1" codename="XML"> <head ID="2"> <filename ID="3">file.rtf</filename> <author ID="4">Vasia Pupkin</author> <email ID="5">ya_vasia@bigmir.net</email> <editor ID="6">Chebotarjov A.</editor> <chars ID="7">456</chars> </head> <body ID="8"> <title ID="9">XML parsing</title> <annotation ID="10">It's just an example.</annotation> <data ID="11"> <par ID="12"> Body text of article. </par> </data> </body> </article> |
В результате мы можем составить такую таблицу ребер (убедитесь, что по этой таблице можно восстановить исходный документ).
ORDER | Источник | Тип ребра | Приемник | Значение |
0 | подэлемент | article.1 | статья про XML | |
1 | article.1 | атрибут | codename | XML |
2 | article.1 | подэлемент | head.2 | |
3 | head.2 | подэлемент | filename.3 | |
4 | filename.3 | текст | file.rtf | |
5 | head.2 | подэлемент | author.4 | |
6 | author.4 | текст | Vasia Pupkin |
Можно также нумеровать атрибуты и исключить еще один столбец, но поскольку событие «прибыл атрибут» не предусмотрено в SAX, то сам процесс такой нумерации потребует дополнительного кодирования в духе foreach.
Кроме того, не показана алгебра документов — то есть способ учета документов как атомарных величин. Возможно, для этого будет создана отдельная таблица традиционного формата с полями DOC_ID, DOC_NAME, DOC_DESСRIPTION, DOC_LASTMOD и в том же духе.
Как видно, это представление легко укладывается в одну SQL-таблицу и может быть сравнительно прямолинейно восстановлено с использованием SAX. Фактически многие «объектные» базы данных работают именно таким образом, сохраняя документы в виде реберного представления и восстанавливая их в момент загрузки.
Реберное представление позволяет также оптимизировать поиск — особенно если таблица индексирована по искомому полю. Например, очень легко найти элемент (ы) с заданным индексом, именем, значением атрибута, связью и так далее.