MarkLogic Server – это документо-ориентированная native XML база данных. Как и в любой документо-ориентированной DB в MarkLogic Server данные можно представить как файлово-фолдерную структуру. Кстати, при доступе к хранилищу через WebDAV данные именно так и представляются. Помимо собственно XML в MarkLogic Server можно хранить и любые бинарные данные в виде файлов.
Внутренне представление XML данных в MarkLogic Server довольно сложное и будет рассмотрено позже. Сейчас же стоит сказать о том, что поместить в MarkLogic Server можно только well formed XML так как хранится он не в виде простого текста, а как объект данных типа XML. Кодировкой внутреннего представления XML данных является Unicode, что избавляет от множества проблем с разными языками. Все Entity в XML данных разворачиваются в цифровые еntity. Если в документе используются только они, то это не доставит никаких проблем, в противном случае MarkLogic Server должен «знать» о всех используемых entity.
Рассмотрим два интересных вопроса – это доступ к документам в MarkLogic Server и наполнение его этими самыми документами.
Далее будем исходить из того, что в базе данных хранятся документы вида
<horse xmlns=”ns1”>
<location>vacuum</location>
<geometry>spherical</geometry>
</horse>
И храниться это будет вот так
/horses/
horse1.xml
horse2.xml
…
Для XPath будем использовать префикс ns1=”ns1”
Первым делом рассмотрим способы получения (чтения) XML документов из базы данных в XQuery коде.
Для того чтобы прочитать один документ следует воспользоваться функцией fn:doc вот так
let $id := "horse1"
let $uri := fn:concat("/horses/", $id, ".xml")
return
fn:doc( $uri )
Указываем путь до документа в качестве параметра и получаем его содержимое
fn:doc(
[$uri as xs:string*]
) as document-node()*
Результатом этой функции является корневой элемент (тег) для XML документа document(), text() элемент для текстового документа и binary() элемент для бинарных документов.
К XML результату этой функции можно применять любой XPath. Например так
fn:doc( $uri )/ns1:horse
или получить список тегов ns1:location
fn:doc( $uri )/ns1:horse/ns1:location
или сделать это так
fn:doc( $uri )//ns1:location
Документы в MarkLogic Server можно объединять в логические группы сохраняя их в разные директории. Например документы (объекты) “horseN.xml” сохранены в директории “/horses/”. Но! Довольно часто требуется создавать пересекающиеся группы (объединения) документов. Для этих целей, а так же для ускорения доступа к документам в MarkLogic Server существует механизм коллекций. Каждый документ может состоять в нескольких коллекциях одновременно, никаких ограничений на это не существует.
Далее рассмотрим способ получения документов из коллекции. Предположим, что наши документы включены в коллекцию “horses-collection”, тогда доступ к коллекции выполняется следующим образом
let $collections := ("horses-collection")
return
fn:collection($collections)
где $collections – это список коллекций документы которых нужно получить
fn:collection(
[$uri as xs:string*]
) as document-node()*
Данная функция возвращает документы включенные в указанные коллекции. Результатом fn:collection является список документов.
Параметр $uri в функции fn:collection не обязателен. При его отсутствии fn:collection возвращает список всех документов в базе данных. Следующее выражение позволяет сделать это
fn:collection()
Стоит заметить, что коллекции в MarkLogic Server не нужно создавать или конфигурировать зарaнее. При добавлении документа в несуществующую коллекцию она создается вместе с ним и документ помещается во вновь созданную коллекцию. Такой подход позволяет создавать коллекции динамически. Это дает еще больше гибкости при организации быстрого доступа к документам в хранилище.
Существует еще один способ доступа к документам, добавленный разработчиками для упрощения кода программ на XQuery
/ns1:horse
данное выражение эквивалентно следующему
fn:collection()/ns1:horse
То есть сканировать все документы в хранилище и вернуть содержимое документов, которые имеют корневой тег ns1:horse.
Получается что в коде XQuery можно писать XPath как отбельный элемент и он будет применен ко всем документам хранилища и вернет результат своего выполнения.
Стоит очень аккуратно использовать такой подход для получения данных из базы потому-что при большом количестве документов это может потребовать значительных ресурсов и времени.
Результатом следующего выражения будут все тот же список документов ns1:horse, но при его выполнении будет просканирован каждый тег в базе, что является очень ресурсоемкой задачей
//ns1:horse
Применение такого способа получения данных оправданно только в том случае когда необходимо выбрать все теги, конкретное расположение которых в XML документах не известно либо не постоянно. Только в таком случае оправданно сканирование всех тегов в хранилище. Не стоит забывать, также, что производительность запросов в таком случае будет на порядок ниже, особенно при большом количестве документов или тегов в них.
Иногда документы сохраняют в одну директорию не объединяя их в коллекцию. Но при этом расположение документов внутри одной директории имеет смысл в логике программы и требует выполнять доступ к этим документам как к единой сущности. Для выполнения такой задачи можно воспользоватся следующим методом
xdmp:directory("/horses /", "1")/ns1:horse
Функция xdmp:directory возвращает все документы в указанной директории
xdmp:directory(
$uri as xs:string*,
[$depth as xs:string?]
) as document-node()*
Здесь необязательная переменная может принимать два значения “1” и “infinity” описывает же оно глубину вложенности документов, которые войдут в результат. В случае $depth=“1” MarkLogic Server ограничится документами в указанной директории, “infinity” же заставит его сканировать все поддиректории в поисках документов.
Все приведенные выше XPath выражения простые, но на их месте может быть что-то похожее на это
//ns1:location[ (fn:starts-with(., “va”) and fn:starts-with(., “m”)) or (. eq “location1”) ]
Сложный XPath сильно увеличивает затрачиваемые на выполнение запроса ресурсы. Организовывать получение документов из базы данных лучше средствами самой DB такими как коллекции и директории используя при необходимости простые XPath выражение.
Второй важный вопрос — это наполнение базы. Сразу оговорюсь, что MarkLogic Server не поддерживает XQuery Update расширение для XQuery и предоставляет функции манипулирования документами через свой API.
Наполнение базы данных можно осуществить несколькими способами:
1. Создать документ прямо из XQuery кода. Делается это примерно так
declare variable $collections := ("horses");
let $uri := “/horses/horse1.xml”
let $horse :=
<horse xmlns=”ns1”>
<location>vacuum</location>
<geometry>spherical</geometry>
</horse>
return
xdmp:document-insert( $uri, $horse, xdmp:default-permissions(), $collections )
Параметры функции xdmp:document-insert выглядят так
xdmp:document-insert(
$uri as xs:string,
$root as node(),
[$permissions as element(sec:permission)*],
[$collections as xs:string*],
[$quality as xs:int?],
[$forest-ids as xs:unsignedLong*]
) as empty-sequence()
где $uri – это адрес документа относительно корня хранилища. В настройках базы данных MarkLogic Server существует возможность включить автоматическое создание директорий и тогда $uri может ссылаться на несуществующую директорию и она будет создана при создании документа.
$root — тело документа
$permissions – настройки доступа документа
$collections – список коллекций в которые должен быть включен документ.
2. Загрузить данные через WebDAV. Этот способ подходит для загрузки довольно больших объёмов данных в хранилище. Для доступа через WebDAV в MarkLogic Server должен быть создан соответствующий (WebDAV) application server для базы данных к которой необходимо получить доступ.
3. Для больших объемов данных или очень специфичных задач можно воспользоваться java утилитой, RecordLoader предназначенной для загрузки документов в MarkLogic Server.
4. AutoLoader это еще одна полезная утилита, позволяющая отслеживать изменения на файловой системе и автоматически загружать документы в MarkLogic Server. Для загрузки используется утилита RecordLoader.
Автор: SleepwalkerOne