- 16 октября 2002 г.
XQuery: что это такое
Последняя статья рубрики посвящена языку запросов XML (XQuery). Мы уже
рассказывали о
работе Международного консорциума W3C над спецификациями XQuery. Эта статья
продолжает традицию, начатую в
предыдущих номерах
Журнала - рассказывать о расширениях языка XML, предназначенных для решения
прикладных задач.
Работа над спецификацией XQuery близка к завершению - Международный консорциум собирается выпустить ее в конце 2002г. XQuery - это мощный и удобный язык, предназначенный для обработки XML. Под XML понимаются не только файлы в XML-формате, но и другие данные, похожие на XML, включая базы данных, структура которых представляет собой вложенные, поименованные деревья с атрибутами.
XQuery - это интересный язык с некоторыми необычными понятиями. Цель этой статьи - предоставить общее представление о XQuery, в ней рассматриваются основные понятия языка, понимание которых необходимо для его дальнейшего изучения и применения.
Язык выражений
Первое, на что следует обратить внимание, это то, что в XQuery любая конструкция - это выражение, результатом вычисления которого является некоторое значение. Программа XQuery или скрипт - это просто выражение вместе с некоторыми необязательными функциями и другими определениями. Поэтому 3+4 - это завершенная, допустимая программа XQuery, которая при вычислении равняется 7.
У стандарта XQuery нет изменений или уточнений, хотя, вероятно, в будущем они появятся. Эта спецификация определяет результирующую величину выражения или программы, но не устанавливает, как ее нужно вычислять. На реализацию практически не накладывается ограничений в отношении того, как она вычисляет программу XQuery и какую оптимизацию использует.
Ниже приведено условное выражение, значением котором является строка:
if (3 < 4) then "yes!" else "no!"
Для описания локальных переменных используется выражение let:
let $x := 5 let $y := 6 return 10*$x+$y
Это выражение равно 56.
Примитивные типы данных
Примитивные типы данных XQuery такие же, как в XML Schema:
- числа, включая целые и числа с плавающей запятой;
- булевы числа: true (истина) и false (ложь);
- строки символов, например, "Hello world!". Строки являются неизменными (immutable), то есть символ в строке не может быть изменен;
- различные типы для представления дат, времени и продолжительности.
- несколько типов, связанных с XML. Например, QName - это часть локального имени (как template) и URL (унифицированный указатель ресурса), который используется для представления имени тега после того, как для него было установлено пространство имен (как xsl:template).
Производные типы являются вариациями или ограничениями других типов. Примитивные типы и типы, полученные из них, известны как атомарные типы, поскольку атомарные величины не содержат других величин. Таким образом, строка считается атомарной, так как в XQuery нет значений символов.
Величины узлов и выражения
В XQuery также есть типы данных, необходимые для представления величин XML. Для этого используются величины узлов следующих семи типов: элемент, атрибут, пространство имен, текст, комментарий, инструкция обработки и документ (корень). Эти типы очень похожи на соответствующие классы DOM: Node, Element и так далее. В некоторых реализациях XQuery используются объекты DOM для задания величин узлов, хотя в реализациях могут применять и другие представления.
Для создания и возврата узлов используются различные стандартные функции XQuery. Так, функция document читает XML-файл, указанный аргументом URL, и возвращает корневой узел документа. (Корневой элемент - это потомок корневого узла).
Новые узлы можно также создавать непосредственно в программе. Наиболее удобный способ - воспользоваться выражением конструктор элемента (element constructor), которое выглядит точно также, как регулярные данные XML:
<p>See <a href="index.html"><i>here</i></a> for info.</p>
Чтобы поместить выражение XQuery внутрь конструкторов элементов следует воспользоваться {} (фигурными скобками). Так,
let $i := 2 return
let $r := <em>Value </em> return
<p>{$r} of 10*{$i} is {10*$i}.</p>
создает
<p><em>Value </em> of 10*2 is 20.</p>
Распространенные процессоры шаблонов, такие как JSP, ASP и PHP, позволяют вставлять выражения на языке программирования в содержимое HTML. Помимо этого XQuery разрешает помещать формы XML/ HTML внутрь выражений и использовать их в качестве значений переменных и параметров.
Величины узлов XQuery являются неизменными (после создания узла его нельзя изменить).
Последовательности
Рассмотренные атомарные величины (числа, строки и т.п.) и величины узлов (элементы, атрибуты и т.д.) известны как простые величины. Результатом вычисления выражения XQuery на самом деле является последовательность простых величин. Например,
3,4,5
это последовательность, состоящая из 3 целых. Заметьте, что если последовательность содержит только одно значение, то она совпадает с самой величиной. Не допускается использовать вложения для последовательностей. Проиллюстрируем это - для этого воспользуемся функцией count, которая принимает один аргумент и возвращает число величин в последовательности. Тогда выражение
let $a := 3,4
let $b := ($a, $a)
let $c := 99
let $d := ()
return (count($a), count($b), count($c), count($d))
равняется (2, 4, 1, 0), потому что $b то же самое, что и (3,4,3,4).
Многие из стандартных функций, предназначенных для работы с узлами, возвращают последовательности. Так, функция children возвращает последовательность узлов-потомков аргумента. Например,
children(<p>This is <em>very</em> cool.</p>)
возвращает последовательность из трех величин:
"This is ", <em>very</em>, " cool."
Выражения Path и отношение к XPath
В XQuery используются path expression XPath. XQuery можно рассматривать как обобщение XPath. За исключением некоторых малоизвестных форм (в основном необычных "осевых спецификаторов") все выражения XPath являются также и выражениями XQuery. По этой причине комитет, занимающийся XQuery, также работает и над спецификацией XPath - планируется, что XQuery 1.0 и XPath 2.0 будут опубликованы одновременно.
Предположим, что корневой элемент <book> XML-файла "mybook.xml" содержится несколько элементов-потомков <chapter>:
let $book := document("mybook.xml")/book
return $book/chapter
Функция document возвращает корневой узел документа. Поскольку выражение /book выбирает элементы-потомки корневого узла, имя которого book, $book равняется одному корневому элементу.
$book/chapter выбирает элементы-потомки элементов верхнего уровня book, в результате чего получается последовательность узлов chapter второго уровня в документальном порядке (document order).
В следующем примере демонстрируется предикат (predicate):
$book//para[@class="warning"]
Двойная косая черта - это синтаксис, предназначенный для выбора всех наследников (а не просто потомков) $book, причем отбираются только узлы элементов <para>, у которых значение узла атрибута по имени class равно "warning".
Необходимо отметить следующее различие между XPath и XQuery - выражения XPath могут вернуть множество узлов (node set), а такое же выражение XQuery возвращает последовательность узлов. Для обеспечения совместимости эти последовательности находятся в документальном порядке, в них удалены дубликаты - благодаря этому они эквиваленты множествам.
XSLT - очень удобен для выражения довольно простых преобразований, но более сложные таблицы стилей (особенно что-либо с нетривиальной логикой или программированием) часто можно записать более аккуратно с помощью XQuery.
Итерация по последовательностям
Выражение for позволяет организовывать цикл по элементам последовательности:
for $x in (1 to 3) return ($x,10+$x)
Сначала вычисляется выражение, находящееся за in. Затем переменной (в данном случае $x) присваивается каждое значение из результирующей последовательности, и выражение return вычисляется для этих значений переменной. Значение всего выражения for - это упорядоченное соединение (конкатенация) всех величин выражения. В этом примере получается последовательность из шести элементов: 1,11,2,12,3,13.
Вернемся к примеру с файлом mybook.xml, предположим, что <book> содержит несколько элементов <chapter>, в каждом из которых находится элемент <title>. Приведенный ниже код создает простую страницу, на которой перечислены заголовки (title):
<html>{
let $book := document("mybook.xml")/book
for $ch in $book/chapter
return <h2>{$ch/title)</h2>
}</html>
Термин "выражение FLWR" относится и к выражению for, и к выражению let. Аббревиатура FLWR расшифровывается как одно или несколько операторов for и/или let, необязательный оператор where и оператор result. Оператор result вычисляется только тогда, когда выражение where истинно (true).
В следующем примере используется вложенный цикл, который позволяет комбинировать две последовательности: одну из клиентских элементов и другую из элементов заказа. В этом фрагменте кода отыскивается имя (имена) клиентов, заказавших деталь с part_id равным "xx".
for $c in customers
for $o in orders
where $c.cust_id=$o.cust_id and $o.part_id="xx"
return $c.name
По существу, это соединение двух таблиц - операция, часто выполняемая в реляционных базах данных. Важно помнить, что XQuery был задуман как язык запросов для баз данных XML. Для сравнения ниже приведена соответствующая инструкция SQL:
select customers.name
from customers, orders
where customers.cust_id=orders.cust_id
and orders.part_id="xx"
Функции
Без функций, определяемых пользователем, XQuery был бы далек от языка от программирования. Определения таких функций располагаются в прологе запроса (query prologue) программы XQuery. Стоит заметить, что параметрами функции и результатами ее вычисления могут быть примитивные типы, узлы или последовательности из тех или других.
Ниже приведена рекурсивная служебная функция. Она возвращает все узлы-наследники аргумента, включая сам узел аргумента. Она проходит вглубь по аргументу и возвращает аргумент, а затем выполняет цикл по потомкам узла аргумента, рекурсивно вызывая саму себя для каждого потомка.
define function descendant-or-self ($x)
{
$x,
for $y in children($x)
return descendant-or-self($y)
}
descendant-or-self(<a>X<b>Y</b></a>)
В результате, получается последовательность глубины 4:
<a>X<b>Y</b></a>; "X"; <b>Y</b>; "Y"
Сортировка и контекст
Для сортировки последовательности используется выражение sortby. Чтобы упорядочить последовательность книг по имени автора можно сделать следующее:
$books sortby (author/name)
Выражение sortby берет входную последовательность (в данном случае $books) и одно или несколько выражений упорядочения (ordering expressions). При сортировке реализация должна сравнивать две величины из входной последовательности, чтобы определить, какая должна идти первой. Для этого вычисляется выражение(я) упорядочения в контексте величины из входной последовательности. Поэтому path expression author/name вычисляется множество раз, каждый раз относительно разной книги, используемой в качестве контекстуальной (или текущей) единицы (context (or current) item).
Path expression также используют и устанавливают этот контекст. В author/name возвращаемые потомки name - это потомки из контекстуальной единицы author.
Определение типов
XQuery - строго типизированный язык программирования. Как Java и C#, XQuery - это смесь статического (проверка совместимости типов во время компиляции) и динамического контроля типов (тестирование типов во время выполнения). Однако, типы в XQuery отличаются от классов, присущих объектно-ориентрованному программированию. Взамен XQuery включает типы, которые соответствуют модели данных XQuery, и позволяет импортировать типы из XML Schema.
if ($child instance of element section)
then process-section($child)
else ( ) {--nothing--}
В этом фрагменте кода функция process-section вызывается, если значением $child является элемент, имя тега которого section. В XQuery для сопоставления величины с набором типов есть удобная сокращенная форма (typeswitch). В следующем примере показывается, как преобразовывать один набор имен тегов в другой набор:
define function convert($x) {
typeswitch ($x)
case element para return <p>{process-children($x)}</p>
case element emph return <em>{process-children($x)}</em>
default return process-children($x)
}
define function process-children($x) {
for $ch in children($x) return convert($ch)
}
Ресурсы
Основной ресурс по XQuery - www.w3.org/XML/Query, здесь находятся ссылки на рабочие версии стандартов, списки рассылки и реализации. Основные документы:
- Сама спецификация XQuery (www.w3.org/TR/xquery/) не сложна для чтения и, вероятно, с нее можно начать.
- В спецификации "Модель данных" (Data Model, www.w3.org/TR/query-datamodel/) описываются узлы и функции для их манипулирования.
- В спецификации "Функции и операторы" (Functions and Operators, www.w3.org/TR/xquery-operators/) определяются другие ("неузловые") функции, в том числе функции, предназначенные для работы со строками и датой/временем.
- В документе "Случаи использования" (Use Cases, www.w3.org/TR/xmlquery-use-cases) содержится множество полезных примеров программ XQuery, предназначенных для решения определенных проблем.
- Спецификация "Формальная семантика" (Formal Semantics, www.w3.org/TR/query-semantics/) оперирует формальными математическими обозначениями, и поэтому она "не для слабых духом" - большинству пользователей ее стоит пропустить.
В настоящий момент XQuery посвящена только одна книга "Первооткрыватели XQuery" (Early Adopter XQuery, издательство Wrox) - главным образом из-за того, что в спецификации остаются неразрешенными еще очень многие вопросы. Автор этой статьи написал вместе с Джеймсом Макговерном (James McGovern), Куртом Кэглем (Kurt Cagle), Джеймсом Линном (James Linn) и Вайдянатаном Нагарьяном (Vaidyanathan Nagarjan) "XQuery: быстрое начало" (XQuery Kick Start), которая должна выйти в издательстве Sams Publishing в 2003г. Кроме того, хотя пока нет стандартных реализаций, на сайте XQuery перечислены существующие реализации, некоторые из которых имеют исполняемые демо-версии. Единственная реализация с открытом кодом, по всей видимости, это разработка самого автора. (Qexo интересна тем, что компилирует на лету программы XQuery непосредственно в байт-код Java). Автор рекомендует обратиться к XQuery в том случае, если требуется мощное и удобное средство для анализа или генерации XML.
Автор: Пер Ботнер (Per Bothner)