Журнал ВРМ World

Мировая история развития технологий управления эффективностью бизнеса – обзоры зарубежных публикаций

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)