[an error occurred while processing this directive]
Дон Чемберлин
Журнал Открытые системы #01/2003
Консорциум World Wide Web Consortium (W3C) образовал рабочую группу для разработки языка запросов к источникам данных, представленных на языке XML. Этот язык запросов, получивший название XQuery, развивается до сих пор и описан в серии предварительных документов. XQuery - функциональный язык, состоящий из нескольких видов выражений, которые могут использоваться в разных сочетаниях. Язык базируется на системе типов XML Schema и совместим с другими стандартами, связанными с XML. В статье объясняются причины создания языка запросов XML, предлагается вводное описание XQuery и приводится несколько примеров его использования.
Язык XML [1] все чаще применяется в качестве формата для обмена информацией между разными приложениями в Internet. Популярность XML во многом объясняется его гибкостью при представлении разных видов информации. Применение тегов делает XML-данные самоописываемыми, а расширяемая природа XML позволяет определять новые виды специализированных документов. По мере роста значимости XML создается целая серия стандартов, многие из которых были подготовлены консорциумом W3C [2]. Так, XML Schema [3] обеспечивает нотацию для определения новых типов элементов и документов; XML Path Language (XPath) [4] - нотацию для выбора элементов в документе XML; Extensible Stylesheet Language Transformations (XSLT) [5] - нотацию для преобразования документов XML из одного представления в другое.
XML позволяет приложениям обмениваться данными в стандартном формате, не зависящем от способа их хранения. Скажем, одно приложение может использовать естественный для XML формат хранения, а другое - хранить данные в реляционной базе данных. Поскольку XML все больше утверждается в роли стандарта для обмена данными, естественно, что запросы, поступающие от приложений, должны быть выражены как запросы к данным в формате XML. Это вызывает потребность в языке запросов, явно ориентированном на источники XML-данных. В октябре 1999 года W3C образовал рабочую группу XML Query Working Group [6] с целью разработки такого языка запросов, получившего название XQuery.
В XML-документах имеется внутренний порядок, а реляционные данные неупорядочены, если не принимать во внимание те случаи, когда порядок можно определить на основе значений данных. Реляционные данные обычно являются <плотными> (т.е. почти в каждом столбце имеется значение), а отсутствующая информация в реляционных системах часто представляется специальным значением null. XML-данные часто бывают <разреженными>, а отсутствие информации может представляться отсутствием элемента. По этим и другим причинам имеющиеся языки реляционных запросов не подходят напрямую для запросов XML-данных.
Разработка XQuery все еще продолжается. XML Query Working Group опубликовала предварительные рабочие версии нескольких документов, описывающие существующее состояние разработки. Возможно, наиболее важным из них является документ XQuery 1.0: An XMLQuery Language [7], содержащий синтаксис и неформальное описание языка. Кроме того, рабочая группа опубликовала список требований [8], описание модели данных, положенной в основу языка [9], формальное описание семантики [10], список функций и операторов [11], а также примеры, иллюстрирующие применение этого языка [12]. Каждый из этих документов обновляется по мере дальнейшего развития XQuery. Данная статья опирается на самую последнюю к моменту публикации версию языка.
На разработку XQuery влияет целый ряд факторов. Возможно, важнее всего совместимость с существующими стандартами W3C, в том числе, XML Schema, XSLT, XPath и сам XML. В частности, язык XPath настолько тесно связан с XQuery, что XQuery определяется как надмножество XPath. Общая структура XQuery базируется на предварительной версии языка, получившей название Quilt [13]. В свою очередь, на создание Quilt оказали влияние функциональный подход языка OQL (Object Query Language) [14], синтаксис на основе ключевых слов языка SQL [15] и предыдущие предварительные версии языка запросов XML, в том числе XQL [16], XML-QL [17] и Lorel [18].
Цель XML Query Working Group - определение двух видов синтаксиса XQuery: один из них выражается на XML, а другой оптимизирован для восприятия человеком. В этой статье описывается только <человеческий> вариант XQuery.
В начальном виде XQuery устремлен только на извлечение информации и не включает средств для модификации существующих документов XML. Возможно, XML Query Working Group займется добавлением средств модификации после завершения работы над первой версией XQuery.
В данной статье описывается модель данных, на которой основан XQuery, а затем представляется обзор языка XQuery в виде серии примеров. Синтаксис XQuery и более полное описание самого языка можно найти в [7]. Модель данных
Формально входные и выходные данные XQuery определяются в терминах модели данных, описанной в [9]. <Запросная> модель данных обеспечивает абстрактное представление одного или нескольких документах или фрагментов XML-документов. Модель данных опирается на понятие последовательности. Последовательность (sequence) - это упорядоченный набор нулевого или большего числа объектов. Объект (item) может быть узлом или атомарным значением. Атомарное значение (atomic value) - экземпляр одного из встроенных типов данных, определенных в XML Schema, таких как строки, целые и десятичные числа, даты. Узел (node) соответствует одному из семи видов: элементы, атрибуты, тексты, документы, комментарии, команды обработки и пространства имен. Узел может иметь другие узлы в качестве потомков, что позволяет образовывать одну или несколько иерархий узлов. Некоторые виды узлов, такие как элементы и атрибуты, имеют имена или типизированные значения, либо и то, и другое. Типизированное значение (typed value) - это последовательность из нуля или большего числа атомарных значений. Узлы индивидуальны (т. е. два узла можно различить, даже если они имеют одинаковые имена и значения), но атомарные величины такой индивидуальностью не обладают. Для всех узлов иерархии имеется полный порядок, называемый порядком документа (document order), в соответствии с которым каждый узел предшествует своему потомку. Порядок документа соответствует порядку, в котором следовали бы узлы, если бы иерархия узлов представлялась в формате XML. Порядок документа между узлами в разных иерархиях определяется в реализации, но он должен быть последовательным, т.е. все узлы одной иерархии должны располагаться либо до, либо после всех узлов другой иерархии.
Последовательности могут быть неоднородными, т.е. могут содержать смесь узлов и атомарных значений разного типа. Однако последовательность никогда не может быть объектом в другой последовательности. Все операции, создающие последовательность, определены так, что результат операции - одноуровневая последовательность. Не проводится различие между объектом и последовательностью единичной длины, т.е. узел и атомарное значение величины считаются идентичными последовательности единичной длины, содержащей эти узел или атомарное значение.
Допускаются последовательности нулевой длины, и иногда они используются для представления отсутствующей или неизвестной информации, во многом так же, как в реляционных системах используются неопределенные значения.
Помимо последовательностей в запросной модели данных определяется специальное значение, называемое значением ошибки (error value), которое является результатом вычисления выражения, содержащего ошибку. Значение ошибки не может присутствовать в последовательности вместе с каким-либо другим значением.
Входные XML-документы могут быть преобразованы в запросную модель данных с помощью процесса, называемого проверкой корректности по схеме (schema validation). Этот процесс выполняет грамматический разбор документа, проверяет его корректность в соответствии с некоторой схемой и представляет документ в виде иерархии узлов и атомарных значений, помеченных типом полученным из схемы [3]. Если входной документ не имеет схемы, проверка его корректности выполняется в соответствии с используемой по умолчанию рекомендательной схемой, которая присваивает родовые типы - узлы маркируются как anyType, а атомарные величины - как anySimpleType.
Результат запроса может быть преобразован из запросной модели данных запросов в XML-представление с помощью процесса, называемого сериализацией (serialization). Следует отметить, что результат запроса не всегда является правильно построенным XML-документом. Например, запрос может возвращать атомарное значение или последовательность элементов, не имеющих общего предка.
Чтобы проиллюстрировать запросную модель данных и обеспечить основу для последующих примеров, рассмотрим небольшую базу данных, содержащую данные интерактивного аукциона и основанную на Use Case R [12]. Эта база данных содержит два XML-документа с именами items.xml и bids.xml.
Документ items.xml содержит корневой элемент с именем items, который, в свою очередь, содержит элемент item для каждого из товаров, предложенных к продаже на аукционе. Каждый элемент item имеет атрибут status и подэлементы с именами itemno, seller, description, reserve-price и end-date. Элемент reserve-price указывает минимальную продажную цену, установленную владельцем товара, а end-date определяет дату окончания торгов.
Документ bids.xml содержит корневой элемент с именем bids, который, в свою очередь, содержит элемент bid для каждой ставки, которая предлагается за товар. Каждый элемент bid имеет подэлементы с именами itemno, bidder, bid-amount и bid-date.
На рис. 1 и 2 показаны модельные представления документов items.xml и bids.xml соответственно (включающие только образцы товара и ставки). Круги, помеченные буквами D, E, A и T, обозначают узлы документов, элементов, атрибутов и тестов соответственно.
Теперь опишем выражения XQuery.
Основы. Подобно XML и XPath, в XQuery различаются прописные и строчные буквы, а все ключевые слова состоят из строчных букв. Подробные лексические и грамматические правила XQuery описаны в [7]. Символы, заключенные между <{-> и <-}> считаются комментариями и при обработке запроса игнорируются (конечно, кроме тех случаев, когда они входят в строку, заключенную в кавычки, и считаются частью этой строки).
Простейший вид выражения XQuery - литерал (literal), который представляет атомарное значение.
47 литерал типа integer 4.7 литерал типа decimal, поскольку он содержит десятичную точку 4.7E3 литерал типа double, поскольку он содержит экспоненту "47" литерал типа string (внутри строки, заключенной в двойные кавычки, разрешается помещать одинарные кавычки)
Атомарные значения других типов могут создаваться путем вызова конструкторов. Конструктор (constructor) представляет собой функцию, которая создает значение определенного типа на основе строки, содержащей лексическое представление значения этого типа. В общем случае конструктор имеет то же имя, что и тип, значения которого он конструирует. Ниже конструктор используется для создания значения типа date.
date(<2002-5-31>)
Любое выражение XQuery может быть заключено в круглые скобки. Скобки полезны для определения явного порядка вычисления выражения.
Операция запятая (<,>) соединяет два значения в последовательность. Последовательности часто заключаются в скобки, служащие явными разделителями, хотя это не требуется. Пустая пара скобок означает пустую последовательность. Поскольку последовательности не могут быть вложенными, оператор <запятая> создает последовательность, состоящую из всех объектов левого операнда, за которыми следуют все объекты второго операнда. Последовательность также можно создать с помощью операции to, производящую последовательность, которая состоит из всех целых чисел в отрезке от значения левого операнда до значения правого. Следующие примеры иллюстрируют создание последовательностей.
1, 2, 3 последовательность из трех значений (1, 2, 3) идентична 1, 2, 3 ((1, 2), 3) идентична 1, 2, 3 1 to 3 идентична 1, 2, 3
Переменная (variable) в XQuery - имя, начинающееся со знака доллара. Переменная может быть связана со значением и использоваться в выражении для представления этого значения. Один из способов связывания переменной состоит в использовании выражения LET, которое связывает одну или несколько переменных, а затем вычисляет внутреннее выражение. Значение выражения LET - результат вычисления внутреннего выражения со связанными переменными. Следующий пример иллюстрирует выражение LET, которое возвращает последовательность 1, 2, 3.
let $start := 1, $stop := 3 return $start to $stop
Выражение LET - частный случай выражения FLWR (for, let, where, return), которое обеспечивает дополнительные способы связывания переменных.
Еще одна простая форма выражений XQuery - вызов функции (function call). XQuery предусматривает наличие базовой библиотеки функций, описанной в [11], и описываемый в следующем разделе механизм, позволяющий пользователям определять дополнительные функции. Вызовы функций в XQuery основаны на обычной нотации с заключением в скобки аргументов функции. В следующем примере вызывается функция базовой библиотеки substring, извлекающая из строки первые шесть символов.
substring(, 1, 6)
Выражения пути. Выражения пути в XQuery базируются на синтаксисе XPath [4]. Выражение пути состоит из серии шагов, разделенных символом слэша (>). Результат каждого шага - последовательность узлов. Значение выражения пути - последовательность узлов, которая формируется на последнем шаге.
Каждый шаг вычисляется в контексте некоторого узла, называемого контекстным узлом (context node). В общем случае шаг может быть любым выражением, возвращающим последовательность узлов. Один из важных видов шага, называемый осевым шагом (axis step), можно считать перемещением от контекстного узла по иерархии узлов в некотором направлении, называемом осью (axis). При перемещении по указанной оси осевой шаг выбирает узлы, которые удовлетворяют критерию выбора. Критерий выбора может выбирать узлы на основе их имен, положения по отношению к контекстному узлу или предикату, базирующемуся на значении узла. В XPath определяются 13 осей, и часть из них или все будут поддерживаться и в XQuery. Пока планируется реализовать в XQuery поддержку шести осей: child, descendant, parent, attribute, self и descendant-or-self.
Выражения пути могут быть записаны в полном или в сокращенном синтаксисе. Полный синтаксис для осевого шага предусматривает указание оси и критерия выбора, разделенных парой двоеточий. Q1 иллюстрирует четырехшаговое выражение пути, оформленное в полном синтаксисе. На первом шаге вызывается встроенная функция document, которая возвращает узел-документ документа items.xml. Второй шаг - осевой шаг, который находит всех потомков узла-документа (<*> выбирает все узлы на данной оси; в данном случае будет выбран единственный узел-элемент с именем items). Третий шаг снова выполняет поиск вдоль оси child, чтобы найти на следующем уровне все элементы-потомки с именем item, которые, в свою очередь, имеют потомков с именем seller и значением (Q1) Перечислить описания всех товаров, предлагаемых к продаже Смитом.
На практике, выражения пути часто записываются с помощью сокращенного синтаксиса. Пожалуй, наиболее важным является то, что спецификатор оси может быть пропущен в том случае, когда используется ось child. Поскольку child является наиболее часто используемой осью, такое сокращение помогает сократить длину многих выражений пути. К примеру, Q1 можно сократить следующим образом:
Разделение двух шагов двойным, а не одинарным слэшем означает, что второй шаг может выполнять поиск в нескольких уровнях иерархии, используя для этого ось descendants, а не одноуровневую ось child. Так, Q2 выполняет поиск элементов description, которые являются потомками (необязательно прямыми) корневого узла данного документа. Результат Q2 - это последовательность узлов-элементов, которые могут, в принципе, быть найдены на различных уровнях иерархии узлов (хотя в нашем примере все узлы description находятся на одном и том же уровне).
(Q2) Перечислить все элементы описания товаров, имеющиеся в документе items.xml.
В выражении пути одинарная точка (<.>) указывает на контекстный узел, а две последовательные точки (<..>) - на предка контекстного узла. Эти нотации представляют собой сокращенное указание осей self и axes соответственно. Имена, присутствующие в выражениях пути, как правило, интерпретируются как имена узлов-элементов, однако если имя имеет префикс <@>, оно интерпретируется как имя узла-атрибута. Это сокращение для шага, который выполняет поиск вдоль оси attribute. Эти аббревиатуры иллюстрируются в Q3, где поиск начинается с узла, связанного с переменной $description, вдоль оси parent к родительскому узлу item, а затем - вдоль оси attribute в поисках атрибута с именем status. Результатом Q3 является единственный узел-атрибут.
(Q3) Найти атрибут статуса для товара, который является предком данного описания товара.
Предикаты. В XQuery предикат (predicate) - это заключенное в квадратные скобки выражение, которое используется для фильтрации последовательности значений. Предикаты часто применяются в шагах выражения пути. Например, в шаге item[seller = Если в результате вычисления предикатного выражения получается булевское значение, то объект-кандидат выбирается в том случае, если значение предикатного выражение равно true. Этот тип предиката иллюстрируется в примере, где выбираются узлы item, имеющие узел-потомок reserve-price, чье значение больше 1000:
Если результатом вычисления предикатного выражения является число, то объект-кандидат выбирается в том случае, если его порядковый номер в списке объектов-кандидатов равен этому числу. Такой тип предиката представлен в примере, где выбирается пятый узел item по оси child:
Если в результате вычисления предикатного выражения получается пустая последовательность, объект-кандидат отвергается. Однако если результат вычисления предикатного выражения представляет собой последовательность, содержащую хотя бы один узел, объект-кандидат выбирается. Такая форма предиката может применяться для проверки существования узла-потомка, удовлетворяющего некоторому условию. Это иллюстрирует пример, где выбираются узлы item, у которых имеется узел-потомок reserve-price, вне зависимости от его значения:
Внутри предикатов часто используется несколько видов операций и функций.
Операции сравнения значений (value comparison operator): eq, ne, lt, le, gt, ge. Эти операции могут сравнивать два скалярных значения, но порождают ошибку, если любой из операндов является последовательностью с длиной, большей единицы. Если один из операндов - узел, то прежде, чем выполнить сравнение, операция сравнения значений извлекает его значение. Например, item[reserve-price gt 1000] выбирает узел item только в том случае, если он имеет в точности один узел-потомок reserve-price со значением, большим 1000.
Общие операции сравнения (general comparison operator): =, !=, >, >=, <, <=. Эти операции могут работать с операндами, которые представляются собой последовательности, при условии неявного наличия семантики <существования> для обоих операндов. Как и операции сравнения значений, общие операции сравнения автоматически извлекают значения узлов. Например, item[reserve-price = 1000] выбирает узел item, если у него имеется хотя бы один узел-потомок со значением, большим 1000.
Операции сравнения узлов (node comparison operator): is и isnot. Эти операторы определяют идентичность двух узлов. Например, $node1 is $node2 принимает значение <истина>, если переменные $node1 и $node2 связаны с одним и тем же узлом (т. е. для обеих переменных узел один и тот же).
Операции сравнения порядка (order comparison operator). Эти операции сравнивают позиции двух узлов. Например, $node1 << $node2 принимает значенией true, если узел, связанный с $node1, в порядке документа встречается раньше, чем узел, связанный с $node2.
Логические операции (logical operator): and и or. Эти операции могут использоваться для объединения логических условий в предикате. Например, следующий предикат выбирает узлы item, имеющие ровно один элемент-потомок seller со значением Отрицание (negation): not. Это скорее функция, а не операция. Она служит для инвертирования булевых величин.
Во всех приведенных примерах имена элементов и атрибутов были простыми идентификаторами. Однако в соответствии с рекомендацией XML Namespace [19], элементами и атрибутам позволяется иметь имена, состоящие из двух частей, где первая часть - префикс пространства имен, за которым следует двоеточие. Имя, имеющее префикс пространства имен, называется QName. Каждый префикс пространства имен должен быть связан с URI (универсальный идентификатор ресурсов), который уникальным образом определяет пространство имен. Это соглашение позволяет каждому приложению определять имена в своем собственном пространстве, не опасаясь коллизий с именами, определенными другими приложениями, что дает возможность однозначно ссылаться на имена, указываемые в различных приложениях. Если бы префикс auction был связан с URI пространства имен нашего приложения для проведения интерактивного аукциона, то шаг item [reserve-price > 1000] мог бы быть записан с помощью QName следующим образом:
Процесс связывания префикса с URI пространства имен описан в предпоследнем разделе. В большинстве наших примеров используются одиночные имена, а не QName. Эти примеры реалистичны, поскольку XQuery обеспечивает способ указания пространства имен для запроса по умолчанию. Этот подход позволяет не использовать в запросах QName, если не нужно ссылаться на имена из других пространств имен.
Конструкторы элементов. Выражения пути - мощное средство, но им свойственно существенное ограничение: они способны выбирать только существующие узлы. В полном языке запросов необходимо наличие средства конструирования новых элементов и атрибутов, а также возможность указания их информационного наполнения и взаимосвязи. Это обеспечивается в XQuery с помощью вида выражения, называемого конструктором элементов (element constructor).
Простейший конструктор элементов создает элемент в полном соответствии с синтаксисом XML. Например, следующее выражение конструирует элемент с именем highbid, имеющий атрибут status и два элемента-потомка с именами itemno и bid-amount.
В этом примере значения элементов и атрибутов - константы. Однако во многих случаях необходимо создавать элемент или атрибут, значением которых является вычисляемое выражение. Выражение, заключенное в фигурные скобки, необходимо вычислить, а не трактовать как символьный текст. В конструкторе элемента это выражение вычисляется и заменяется своим значением. В следующем примере значения элементов и атрибутов вычисляются. Переменные $s, $i и $bids, используемые в этих выражениях, должны быть связаны с некоторыми выражениями.
В следующем примере конструктор элементов содержит выражение, заключенное в фигурные скобки, которое генерирует один атрибут и два подэлемента. Переменная $b должна быть связана с некоторым выражением.
Узел-элемент, созданный конструктором элемента, является новым узлом, обладающим собственной индивидуальностью. Если, как в приведенном примере, вновь созданный элемент имеет узлы-потомки и атрибуты, порожденные из существующих узлов, то новые узлы-потомки и атрибуты являются копиями узлов, из которых они были получены, но как узлы они индивидуальны.
В приведенных примерах конструкторов элементов, хотя содержимое элементов может быть вычисляемым, имя конструируемого элемента - известная константа. Однако иногда необходимо сконструировать элемент, имя которого, как и его содержимое, вычисляется. Для этого в XQuery определяется специальный вид конструктора, называемого вычисляемым конструктором элемента (computed element constructor). Он состоит из ключевого слова element, за которым следуют два выражения в фигурных скобках - первое вычисляет имя элемента, а второе - его содержимое.
Чтобы привести пример использования вычисляемого конструктора, предположим, что переменная $e связана с элементом, имеющим числовое значение. Нам нужно сконструировать новый элемент, имеющий то же имя, что и $e, и те же атрибуты, что у $e, но его значение должно быть вдвое больше значения $e. Этого можно добиться с помощью выражения, в котором функция data используется для получения числового значения исходного узла.
Подобно вычисляемому конструктору элемента, в XQuery обеспечивается вычисляемый конструктор атрибута (computed attribute constructor), состоит из ключевого слова attribute, за которым следуют два выражения в фигурных скобках - первое вычисляет имя атрибута, а второе - значение. Конструктор атрибута может использоваться везде, где допустим атрибут. Следующий конструктор атрибута на основе связанной переменной $p мог бы сгенерировать атрибут, который выглядит как father = Итерация и сортировка. Итерация - важная часть языка запросов. XQuery предлагает способ выполнять итерацию над последовательностью значений, по очереди связывая переменную с каждым значением и вычисляя выражения для каждого связывания переменной.
В наиболее простой форме итерация в XQuery задается оператором for, в котором указывается имя переменной и предоставляется последовательность значений, над которой переменная итерируется. Далее указывается оператор return, который содержит выражение, вычисляемое для каждого связывания переменной; см. ниже.
Результатом этого итеративного выражения будет последовательность (3, 4).
В операторе for можно указывать более одной переменной с последовательностью итерации для каждой из них. Такой оператор порождает кортежи связываний переменных, которые образуют декартово произведение итерационных последовательностей. Если не указано иное, кортежи связываний генерируются в порядке, сохраняющем порядок итерационных последовательностей, с использованием самой левой переменной как <самый внешний цикл>, а самую правую - как <самый внутренний цикл>. В примере оператор for содержит две переменные и две итерационные последовательности.
В результате получается следующая последовательность из четырех элементов.
Операторы for и let - частные случаи более общего выражения, называемого FLWR. В наболее общем виде выражение FLWR может иметь несколько операторов for, несколько операторов let, необязательный оператор where, а также оператор return. Функция операторов for и let - связывание переменных. Каждый из них содержит одну или несколько переменных и выражение, присваиваемое каждой переменной. Результатом вычисления выражений являются последовательности, и выражения могут содержать ссылки на переменные, для которых связывание было выполнено в предыдущих операторах. Оператор for итерирует каждую переменную над ассоциированной с ней последовательностью, связывая переменную по очереди с каждым объектом последовательности, а как оператор let связывает каждую переменную сразу со всей ассоциированной последовательностью. Это различие иллюстрируется следующей парой операторов.
Эта пара операторов не является полным выражением FLWR, поскольку в нем отсутствует условие return. Операторы for и let просто порождают последовательность кортежей связываний. Приведенный выше пример порождает следующую последовательность из трех пар связываний.
В общем случае, число кортежей связывания, порождаемых серией операторов for и let, равняется произведению мощностей выражений итерации в операторах for. Оператор let при отсутствии оператора for, конечно, порождает только один кортеж связывания.
Кортежи связывания, порожденные операторами for и let в FLWR-выражении, фильтруются в соответствии с необязательным условием where. Оператор where содержит выражение, которое вычисляется для каждого кортежа связывания. Если значением выражения where являются булевское значение true или непустая последовательность (<проверка существования>), то кортеж связываний принимается; в противном случае он отвергается.
Затем в выражении FLWR вычисляется оператор return по очереди и по одному разу для каждого оставшегося после проверки условия where кортежа связывания. Результаты вычислений объединяется в последовательность, которая и является результатом выражения FLWR.
Возможности FLWR иллюстрируются в запросе к базе данных аукциона Q4.
(Q4) Для каждого товара, который имеет более десяти ставок, создать элемент popular-item, содержащий номер товара, описание и число ставок.
Операторы for и let порождают пару связывания для каждого item в items.xml. В каждой паре связывания $i связан с товаром, а $b - с последовательностью, содержащей все ставки для этого товара. Оператор where оставляет только те связанные кортежи, в которых $b содержит более десяти ставок. Затем оператор return для каждого из этих связываний генерирует выходной элемент, содержащий номер товара, описание и число ставок.
По умолчанию, порядок выходной последовательности выражения FLWR соответствует порядку итерационных последовательностей. Перед любым выражением может стоять префиксная операция unordered, указывающая, что порядок результата не имеет значения. Такое указание повышает гибкость реализации, позволяя оптимизировать вычисление выражения.
Конечно, каждое выражение упорядочивания должно возвращать единственный результат, и эти результаты должны быть сравнимы с помощью оператора gt. В случае применения условия sortby пустая последовательность может считаться либо больше любого другого значения, либо меньше любого другого значения - как то определит пользователь.
Условие sortby часто полезно для переупорядочивания результатов выражения FLWR. Если необходимо отсортировать по убыванию bid-count элементы popular-item, сгенерированные в запросе Q4, то в конец Q4 можно добавить следующий оператор.
Важно понимать, что sortby не является частью выражения FLWR, а представляет собой отдельный вид выражений XQuery, который может использоваться для переупорядочивания любой последовательности, вне зависимости от того, сгенерирована она выражением FLWR или нет. Однако если после выражения FLWR стоит sortby, интеллектуальный оптимизатор поймет, что переупорядочивание выходных объектов снимает обычные ограничения на порядок кортежей связываний.
Q4 показывает, как выражение FLWR может походить на запрос с соединением в системе управления реляционной базой данных, а также на запрос с группировкой. Q4 похож на запрос с соединением, поскольку в нем коррелируются элементы, находящиеся в двух разных файлах - items.xml и bids.xml. Он также напоминает запрос с группировкой, поскольку ставки группируются по номеру товара, и вычисляется число ставок в каждой группе.
Арифметика. В XQuery обеспечиваются обычные арифметические операции: +, - , *, div и mod, а также агрегатные функции sum, avg, count, max и min, которые применяются к последовательности чисел и возвращают числовой результат. Оператор деления в XQuery называется div, чтобы его можно было отличить от слэша. Если после оператора вычитания следует имя, перед ним должен стоять пробел, который позволяет отличить его от дефиса, поскольку в XML дефис - корректный символ для имени.
Арифметические операции определяются для числовых значений. К числовым значениям относятся значения типов integer, decimal, float, double или типов, производных от них. Если типы операндов арифметической операции различны, операнды приводятся к ближайшему общему типу в соответствии с иерархией приведения integer -> decimal -> float -> double. Если операнд арифметического оператора является узлом, то автоматически извлекается его типизированное значение.
Важный частный случай - применение арифметических операций к пустым последовательностям. В XQuery пустая последовательность иногда используется для представления отсутствующей или неизвестной информации, во многом подобно тому, как неопределенное значение используется в реляционных системах. По этой причине операции +, -, *, div и mod определяются таким образом, что они возвращают пустую последовательность, если любой из операндов - пустая последовательность. Для иллюстрации этого правила предположим, что переменная $emps связана с последовательностью элементов emp, каждый из которых представляет сотрудника и содержит элементы name и salary, а также дополнительные элементы comission и bonus. Выражение в Q5 преобразует эту последовательность в последовательность элементов emp, каждый из которых содержит элементы name и pay, причем значение pay равно полной заработной плате сотрудника. Для тех сотрудников, комисионные (commission) или премия (bonus) которых не заданы ($e/commission или $e/bonus - пустая последовательность), генерируемый элемент pay будет пустым.
(Q5) Задана последовательность элементов emp. Заменить их подэлементы salary, commission и bonus на новый элемент pay, содержащий сумму значений исходных элементов, а результирующую последовательность отсортировать по убыванию значений элемента pay.
Иногда желательно определять значение по умолчанию, которое может заменять пропущенные операнды в арифметических выражениях. Ниже объясняется, как в этом случае может использоваться функция, определенная пользователем.
Операции над последовательностями. Оператор intersect порождает последовательность, в которую включены все узлы, имеющиеся в обоих операндах. Оператор except позволяет получить последовательность, содержащую все узлы, которые есть в первом операнде, но отсутствуют во втором.
Операторы union, intersect и except возвращают последовательность узлов в порядке документа и удаляют дубликаты из получившихся последовательностей с учетом индивидуальности узлов. Запрос Q6 является примером использования оператора intersect.
(Q6) Создать новый элемент с именем recent-large-bids, содержащий копии всех элементов bid документа bids.xml, которые имеют bid-amount со значением больше 1000 и bid-date со значением позже 1 января 2002 года.
Выражения, в которых используются операции union, intersect и except, часто можно представить в другом виде. Так, запросу Q6 эквивалентен следующий запрос.
Важно помнить о том, что intersect и except бессмысленно использовать для комбинирования узлов разных документов, поскольку узлы в разных документах никогда не могут быть идентичными. Рассмотрим следующий запрос.
В этом запросе операция except применяется к двум последовательностям узлов itemno. Поскольку последовательности узлов выбираются из различных документов, ни один узел во второй последовательности не может быть идентичен узлу из первой последовательности. Результатом запроса будет последовательность всех узлов itemno документа items.xml. Если предполагалось с помощью этого запроса получить список элементов itemno для товаров, которые не имеют ставок, то можно добиться этого воспользовавшись библиотечной функцией empty, которая возвращает true, если ее операнд - пустая последовательность.
В этом примере предикат itemno eq $i/itemno сравнивает два узла itemno, извлекая и сравнивая их содержимое, а не проверяя их идентичность.
Операция |, оставленная для совместимости с XPath 1.0, эквивалентна операции union. Эти операции иногда применяются в шагах выражения пути. Например, следующее выражение пути находит объединение всех потомков b и потомков c узлов в последовательности, связанной с $a; узлы в этом объединении затем используются в качестве контекстных узлов для следующего шага в пути.
Условные выражения. Условные выражения обеспечивают возможность выполнения одного из двух выражений в зависимости от значения третьего выражения. Это записывается в знакомом формате if...then...else, поддерживаемом во многих языках. Требуется наличие всех трех условий (if, then и else), а выражение в условии if должно быть заключено в скобки. Результат всего условного выражения зависит от значения выражения в условии if, называемого выражением проверки (test expression). Правила таковы.
Если значением выражения проверки являются булевское значение true или непустая последовательность (используемая как <проверка существования>), то выполняется оператор then.
Если значением выражения проверки являются булевское значение false или пустая последовательность, то выполняется оператор else.
В противном случае условное выражение возвращает значение ошибки.
Следующее простое условное выражение может быть использовано для получения стоимости товара, в зависимости от существования атрибута с именем discounted.
Q7, представленный на Рис. 3, - пример более сложного запроса, содержащего условное выражение. Этот запрос также иллюстрирует несколько уровней вложенности выражений FLWR и конструкторов элементов.
Рис.3.
Кванторные выражения. Кванторные выражения позволяют проверить некоторое условие, устанавливая, истинно ли оно для некоторого значения последовательности (называется квантором существования) или для всех значений последовательности (называется квантором всеобщности). Результатом всегда является true или false.
Как и FLWR-выражение, кванторное выражение позволяет переменной выполнять итерацию над объектами в последовательности; выполняется поочередное связывание этой переменной с каждым элементом последовательности. Для каждого связывания переменной вычисляется проверочное выражение. Кванторное выражение, которое начинается с some, возвращает значение true, если выражение проверки истинно для некоторого связывания переменной.
Кванторное выражение, начинающееся с every, возвращает значение true, если выражение проверки истинно для всех связываний переменной. Например, следующее кванторное выражение возвращает значение false, поскольку выражение проверки истинно не для всех связываний.
Использование кванторных выражений иллюстрируется запросом Q8.
(Q8) Найти товары в items.xml, для которых все полученные ставки более чем вдвое превысили начальную цену. Получить копии всех таких элементов item, и поместить их в новый элемент с именем underpriced-items.
В XQuery предусмотрена библиотека предопределенных функций [11], а также предоставляется возможность определения пользователями их собственных функций. При вызове аргументы связываются с параметрами функции, и выполняется ее тело, порождая результат вызова функции. Если тип параметра функции не указан, этот параметр может принимать значения любого типа. Если не указан тип результата функции, то функция может возвращать значение любого типа.
В следующем примере определена функция с именем highbid, в качестве параметра использующая узел-элемент и возвращающая десятичное значение. Функция интерпретирует свой параметр как элемент item и извлекает номер товара. Затем она находит и возвращает самую крупную ставку (bid-amount), которая была зафиксирована для товара с этим номером.
Типы, используемые при объявлении типов аргументов и результата в определении функции, могут быть простыми, как decimal, или более сложными типами, например, элементами или атрибутами.
В XQuery не поддерживается перегрузка функций, определенных пользователем, т. е. не допускается наличие двух функций с одинаковыми именами. Тем не менее, некоторые встроенные функции являются перегруженными. Например, функция string может преобразовывать в строку аргумент почти любого типа.
Аргументы при вызове функции должны соответствовать объявленным типам параметров функции. С этой целью аргумент функции числового типа может быть приведен к объявленному типу параметра с помощью иерархии приведения integer -> decimal -> float -> double. Аргумент также считается удовлетворяющим условию вызова, если тип этого аргумента является производным типом (т.е. подтипом) объявленного типа параметра. Если функция, ожидающая атомарное значение в качестве параметра, вызывается с аргументом, являющимся элементом, то до передачи аргумента функции из него извлекается типизированное значение элемента и проверяется на совместимость с ожидаемым типом параметра. Значение, производимое телом функции, должно также соответствовать возвращаемому типу, объявленному в определении; используются обычные правила проверки соответствия типов параметров.
Следующий пример иллюстрирует, как пользователь может написать функцию, которая предоставляет значение по умолчанию для отсутствующих данных. Функция с именем defaulted принимает два параметра: узел-элемент (возможно, отсутствующий) и значение по умолчанию. Если элемент присутствует и имеет непустое значение, функция возвращает это значение. Если же элемент отсутствует или пуст, функция возвращает значение по умолчанию.
С помощь этой функции запрос Q5 можно переписать (здесь отсутствующие или пустые элементы commission и bonus считаются равными нулю).
Функция, в теле которой присутствует вызов самой себя, называется рекурсивной (recursive), и две функции, в теле каждой из которых присутствует вызов другой функции пары, называются взаимно рекурсивными (mutually recursive). В следующем примере рекурсивная функция depth может быть вызвана для элемента и возвращает глубину элемента в иерархии, начинающейся с аргумента вызова. Если у элемент-аргумента отсутствуют потомки, глубина иерархии равна единице. Иначе глубина иерархии на единицу больше максимального значения глубины всех иерархий, корнем которых является потомок элемента-аргумента. Это значение вычисляется путем рекурсивного вызова функции depth.
При создании запроса иногда необходимо сослаться на некоторый тип. Например, как уже было отмечено, при определении функции требуется описать типы параметров функции и ее результата. В других видах выражений XQuery также требуется возможность ссылаться на некоторые типы.
Один из способов сослаться на тип - это указать его квалифицированное имя, или QName. QName может указывать на встроенный тип, такой как xs:integer, или на тип, который определен в некоторой схеме, такой как abc:address. Если в QName имеется префикс пространства имен (часть, расположенная слева от двоеточия), этот префикс должен быть привязан к некоторому идентификатору пространства имен. Это связывание достигается путем описываемого в следующем разделе объявления пространства имен в прологе запроса.
Еще один способ сослаться на тип - сделать это с помощью родового ключевого слова, такого как element или attribute. За этим ключевым словом может следовать QName, которое в большей степени ограничивает имя или тип узла. Например, element обозначает любой элемент, element shipto - любой элемент с именем shipto; и element of type abc:address означает элемент, тип которого - address, объявленный в пространстве имен abc. Ключевое слово attribute обозначает любой атрибут, node - любой узел, а item - любой объект (узел или атомарное значение).
В XQuery также предусмотрен дополнительный синтаксис, который позволяет ссылаться на другие виды узлов и на типы элементов, которые определены в локальной части схемы. Например, element city in customer/address указывает на элемент с именем city, как это определено в контексте схемы customer/address.
За ссылкой на тип может следовать один из трех индикаторов присутствия: <*> означает <ноль или больше>, <+> означает <один или больше>, а > означает <ноль или один>. Отсутствие индикатора присутствия означает присутствие ровно одного экземпляра указанного типа. Проиллюстрируем использование индикаторов присутствия.
Ссылки на тип появляются не только в определениях функции, но и других местах. Одно из таких мест - второй операнд операции instance of, которая возвращает true, если ее первый операнд является экземпляром типа, указанного во втором операнде. Следующие примеры иллюстрируют использование операции instance of (предполагается, что префикс xs привязан к пространству имен схемы http://www.w3.org/2001/XMLSchema).
Первая часть typeswitch состоит из выражения, тип которого проверяется (выражение операнда, operand expression), и необязательной переменной, связываемой со значением выражения операнда. Далее следуют одно или несколько операторов case, каждый из которых содержит тип и выражение. Операнд выражения по очереди проверяется на соответствие типу, указанному в каждом из условий case. Первый оператор case, для которого операнд выражения является экземпляром заданного типа, называется действующим случаем (effective case); выражение в этом операторе case вычисляется и служит результатом typeswitch. Если выражение операнда не соответствует ни одному из типов, указанных в условиях case, результат typeswitch берется из последнего оператора, действующего по умолчанию.
Проиллюстрируем использование typeswitch. Это выражение может появиться в цикле, где переменная $customer итерируется над множеством элементов customer, каждый из которых имеет подэлемент с именем billing-address. Подэлементы billing-address могут относиться к нескольким различным типам, каждый из которых требуется обрабатывать особым образом. Переменная $a связывается с billing-address, а затем вычисляется одно из нескольких выражений, в зависимости от динамического типа $a. В каждом операторе case $a имеет особый тип, например, в первом условии case типом $a должен быть element of type USAddress. Если выясняется, что элемент billing-address не соответствует ни одному из ожидаемых типов, результатом выражения является Имена типов также используются в трех внешне похожих выражениях XQuery, называемых cast, treat и assert. Каждое из этих выражений содержит ключевое слово, ссылку на тип и выражение, заключенное в скобки.
Выражение cast служит для преобразования результата выражения к одному из встроенных типов XML Schema. Поддерживается предопределенный набор преобразований. Например, результат выражения $x div 5 может быть приведен к типу xs:double с помощью выражения cast as xs:double($x div 5). В случае неудачного выполнения операция приведения типа может вернуть значение ошибки. Например, выполнение cast as xs:integer($mystring) будет успешным, если $mystring - строковое представление integer, но вернет ошибку, если $mystring имеет значение Выражение treat позволяет гарантировать, что динамический (времени исполнения) тип выражения соответствует предполагаемому типу. Например, предположим, что статическим(времени компиляции) типом выражения $customer/shipping-address является Address. Некоторое подвыражение может иметь смысл только для значений, соответствующих подтипу Address, такому как USAddress. Создатель подвыражения может использовать выражение treat для объявления ожидаемого типа подвыражения.
В отличие от cast, выражение treat на самом деле не меняет тип операнда. Выполнение происходит в два этапа: (1) операнду присваивается некоторый статический тип, который может использоваться для проверки типа при компиляции запроса; (2) во время выполнения, если реальное значение выражения не соответствует указанному типу, возвращается значение ошибки.
Чтобы понять, как процессор запросов мог бы использовать информацию, предоставляемую выражением treat, рассмотрим следующий пример.
Компилятор XQuery, проверяющий типы, мог бы решить, что в этом примере имеется ошибка типа, поскольку статическим типом $customer/billing-address является Address, а тип Address, в общем случае, не имеет подэлемента zipcode. Однако в следующей формулировке статический тип выражения меняется на USAddress, у которого есть подэлемент zipcode, и ошибка типа исчезает.
Как и treat, выражение assert используется для предоставления процессору запросов информации, которая может оказаться полезной для проверки типов. Выражение assert сообщает процессору запросов, что его выражение операнда имеет некоторый статический тип. Если процессор производит статическую проверку типов, он может породить ошибку, если окажется не в состоянии проверить, что данное выражение соответствует заявленному типу. Выражение assert является более строгим, чем выражение treat, поскольку оно относится к статическому типу выражения и, следовательно, не зависит от входных данных и может быть проверено перед выполнением запроса. С другой стороны, выражение treat относится к динамическому типу выражения и, как следствие, зависит от входных данных и может быть проверено только при выполнении запроса.
В следующем примере будет генерироваться ошибка типа времени компиляции, если статическим типом $customer/billing-address является Address.
В XQuery не требуется, чтобы в реализации поддерживалась статическая проверка типов. Для процессора запросов, который не обеспечивает статическую проверку, assert эквивалентно treat.
Процесс проверки корректности по схеме определен в [3]. Она может выполняться применительно к документу XML или к части документа, например, отдельному элементу.
В запросной модели данных с каждым узлом-элементом ассоциируется аннотация типа. Аннотация типа свидетельствует о том, что данный элемент прошел проверку на соответствие определенному заявленному типу. Элементы, которые не прошли проверку или не соответствуют заявленному типу, получают аннотацию родового типа anyType. Например, элемент, создаваемый конструктором элементов, имеет аннотацию anyType до тех пор, пока не получит более конкретный тип с помощью выражения validate. Ниже конструируется элемент и проверяется в соответствии со схемой (схемами), которые указываются в прологе запроса.
Аннотация типа используется в выражениях, проверяющих тип элемента, таких как instance of и typeswitch, и в выражениях, требующих элемента конкретного типа, таких как вызовы функций. Так, проверка элемента shipto может присвоить ему аннотацию типа USAddress, которая может позволить использовать его в качестве аргумента вызова функции, типом параметра которой является element of type USAddress.
В XQuery запрос состоит из двух частей, называемых прологом запроса (query prolog) и телом запроса (query body). Пролог состоит из серии объявлений, которые определяют среду для обработки тела. Тело запроса - просто выражение, чье значение определяет результат запроса.
Пролог нужен только в том случае, если тело зависит от одного или нескольких пространств имен, схем или функций. Если такая зависимость существует, объекты, от которых зависит тело запроса должны быть объявлены в прологе запроса. Мы обсудим по отдельности объявления для пространства имен, схем и функций.
В объявлении пространства имен определяется префикс пространства имен и указывается его привязка к URI пространства имен. Префикс может быть любым идентификатором. В следующем объявлении пространства имен определяется префикс xyz и указывается его привязка.
Это объявление позволяет использовать префикс xyz в именах QName в теле запроса. Префикс связывается с URI некоторого пространства имен и служит в качестве уникального квалификатора для имен элементов, атрибутов и типов. Например, xyz:billing-address может уникально идентифицировать элемент billing-address, определенный в пространстве имен http://www.xyz.com/example/names. С одним пространством имен можно связать несколько префиксов.
В прологе запроса можно объявить пространство имен по умолчанию, применяемое ко всем неквалифицированным именам элементов и топов, и еще одно пространство имен по умолчанию, применяемое ко всем неквалифицированным именам функций. Ниже иллюстрируется синтаксис объявления пространств имен по умолчанию.
Если пространства имен по умолчанию не введены, то неквалифицированные имена элементов, типов или функций считаются не относящимися к какому-либо пространству имен. Неквалифицированные имена атрибутов всегда считаются не относящимися к какому-либо пространству имен.
Помимо объявлений пространств имен пролог запроса может содержать одно или несколько объявлений импорта схемы. При объявлении импорта схемы указывается URI схемы, а также может быть указан второй URI, определяющий место, где может быть найден файл схемы. Цель импорта заключается в том, чтобы предоставить процессору запросов определения элементов, атрибутов и типов, которые объявлены в указанной схеме. Процессор запросов может использовать эти определения для проверки вновь сконструированных элементов, для оптимизации и для проведения статического анализа типов в запросе.
В схеме набор элементов, атрибутов и типов обычно определяется в некотором пространстве имен, называемом целевым пространством имен (target namespace) схемы, но префикс пространства имен не определяется. Поэтому при импорте схемы можно указать префикс пространства имен, привязанный к целевому пространству имен этой схемы. В следующем объявлении импорта схемы префикс пространства имен xhtml связывается с целевым пространством имен некоторой схемы, а также системе предоставляется отдельная <подсказка>, позволяющая найти эту схему.
Помимо объявлений пространства имен и импорта схем пролог запроса может содержать одно или несколько определений функции. Функции, определенные в прологе запроса, могут использоваться в теле запроса или в телах других функций.
XQuery определяется в терминах модели данных, основанной на неоднородных последовательностях узлов и атомарных значениях. Экземпляр этой модели данных может содержать один или несколько документов или фрагментов документов XML. Запрос обеспечивает отображение одного экземпляра модели данных на другой экземпляр. Запрос состоит из пролога, который устанавливает среду обработки, и выражения, которое генерирует результат запроса.
В настоящее время XQuery определен на уровне предварительных рабочих документов; созданием языка продолжает заниматься W3C XML Query Working Group. Эта рабочая группа активно обсуждает систему типов XQuery и вопрос о взаимном отображении этой системы типов и системы типов XML Schema. Также обсуждаются функции полнотекстового поиска, сериализация результатов запроса, обработка ошибок и ряд других вопросов. Скорее всего, окончательная спецификация XQuery будет включать в себя несколько уровней соответствия; например, в ней может быть определено, как следует производить статическую проверку типов, но не будет требоваться, чтобы она выполнялась в каждой реализации, соответствующей спецификации. Также ожидается, что подмножество XQuery будет объявлено как XPath версии 2.0, и станет возможным встраивание этого подмножества в другие языки, такие как XSLT [5].
Более полное описание XQuery и его грамматику в форме Бекуса-Науэра можно найти в [7]. Язык продолжает развиваться, поэтому спецификация XQuery может измениться.
Подобно тому, как XML применяется в качестве универсального формата обмена информацией в Сети, XQuery призван служить в качестве универсального формата обмена запросами. Если XQuery получит признание в качестве стандартного средства извлечения информации из источников XML-данных, это поможет реализовать потенциал XML.
Дон Чемберлин - сотрудник исследовательского центра IBM Almaden Research Center; известен как один из разработчиков языка SQL и автор двух книг по реляционным базам данных. Сейчас деятельность Чемберлина связана с технологиями реляционных баз данных, обработкой документов и XML. Он является представителем IBM в рабочей группе W3C XML Query Working Group и редактором спецификации XQuery.
Don Chamberlin, Xquery: An XML query language. IBM Systems Journal, Vol. 41, No. 4, 2002. Copyright 2002, International Business Machines Corporation. All rights reserved. Reprinted with permission.
[an error occurred while processing this directive]
document("items.xml")/child::*
/child::item [child::seller = "Smith"]
/child::description
document("items.xml")
/*/item[seller = "Smith"]/description
document(
$description/../@status
item [reserve-price > 1000]
item [5]
item [reserve-price]
item [seller eq
auction:item [auction:reserve-price > 1000]
<highbid status = "pending">
<itemno>4871</itemno>
<bid-amount>250.00</bid-amount>
</highbid>
<highbid status = "{$s}">
<itemno> {$i} </itemno>
<bid-amount>
{max($bids[itemno = $i]/bid-amount)}
</bid-amount>
</highbid>
<highbid>
{
$b/@status
$b/itemno
$b/bid-amount
}
</highbid>
element
{name($e)}
{$e/@*, data($e)*2}
attribute
{if $p/sex = "M" then "father" else "mother"}
{$p/name}
for $n in (2, 3) return $n + 1
for $m in (2, 3), $n in (5, 10)
return <fact>{$m} times {$n} is
{$m * $n} </fact>
<fact>2 times 5 is 10 </fact>
<fact>2 times 10 is 20 </fact>
<fact>3 times 5 is 15 </fact>
<fact>3 times 10 is 30 </fact>
for $i in (1 to 3)
let $j := (1 to $i)
$i = 1, $j = 1
$i = 2, $j = (1,2)
$i = 3, $j = (1,2,3)
for $i in
document("items.xml")/*/item
let $b := document("bids.xml")
/*/ bid[itemno = $i/itemno]
where count ($b) > 10
return
<popular-item>
{
$i/itemno,
$i/description,
<bid-count> {count
($b)}</bid-count>
}
</popular-item>
sortby bid-count descending
for $e in $emps
return
<emp>
{
$e/name,
<pay> {$e/salary + $e/commission
+ $e/bonus} </pay>
}
</emp>
sortby (pay)
<recent-large-bids>
document("bids.xml")
/*/ bid [bid-amount > 1000.00]
intersect
document("bids.xml")
/*/ bid [bid-date >
date("2002-01-01")]
</recent-large-bids>
<recent-large-bids>
document("bids.xml")/*/bid
[bid-amount > 1000.00 and bid-date
> date("2002-01-01")]
</recent-large-bids>
document("items.xml")//itemno
except
document("bids.xml")//itemno
for $i in document("items.xml")//item
where empty(document("bids.xml")
//bid [itemno eq $i/itemno])
return $i/itemno
$a/(b | c)/d
if ($part/@discounted) then $part/wholesale
else $part/retail
Q7) Создать отчет, описывающий состояние
ставок для различных товаров.
Пометить каждую ставку статусом <OK,> <too
small> или <too late>.
Поместить отчет в элемент с именем bid-status-report.
<bid-status-report>
for $i in document (<items.xml>)/*/item
return
<item>
{
$i/itemno,
for $b in document (<bids.xml>)/*/bid[itemno = $i/itemno]
return
<bid>
{
$b/bidder,
$b/bid-amount,
<status>
{
if ($b/bid-date > $i/end-date) then <too late>
else if ($b/bid-amount < $i/reserve-price)
then <too small>
else <OK>
}
</status>
}
</bid>
}
</item>
</bid-status-report>
some $n in (5,7,9,11)
satisfies $n > 10
every $n in (5,7,9,11)
satisfies $n > 10
<underpriced items>
for $i in document("items.xml")
where every $b in document("bids.xml")
/*/bid [itemno = $i/itemno]
satisfies $b/bid-amount
> 2 * $i/reserve-price
return $i
</underpriced-items>
Функции
define function highbid(element $item)
returns decimal
{
max(document("bids.xml")
//bid [itemno = $item/itemno]/bid-amount)
}
highbid(document("items.xml")
//item [itemno = "1234"])
define function defaulted
(element? $e, anySimpleType $d)
returns anySimpleType
{
if (empty($e))then $d
else if (empty($e/_)then $d
else data($e)
}
for $e in $emps
return
<emp>
{
$e/name,
<pay> | {$e/salary
+ defaulted ($e/commission,0)
+ defaulted ($e/bonus,0)}
</pay>
}
</emp>
sortby(pay)
define function depth(element $e)
returns integer
{
if (empty($e/*)) then 1
else 1 + max
(for $c in $e/* return depth($c))
}
depth(document("bids.xml"))
Типы
element memo?означает возможное появление элемента с именем memo
element of type order+означает один или несколько элементов с типом order
element*означает любое число любых элементов
attribute?означает необязательный атрибут с любым именем и типом 49 instance of xs:integerвозвращает true
"Hello" instance of xs:integerвозвращает false
<partno>369</partno> instance of element*возвращает true
$a instance of element shiptoвозвращает true, если $a привязана к элементу с именем shipto typeswitch ($customer/billing-address) as $a
case element of type USAddress
return $a/state
case element of type CanadaAddress
return $a/province
case element of type JapanAddress
return $a/prefecture
default return "unknown"
treat as USAddress($customer/billing-address)
$customer/billing-address/zipcode
(treat as USAddress
($customer/billing-address))/zipcode
(assert as USAddress
($customer/billing-address))/zipcode
Проверка корректности
validate {<shipto>
<street>123 Elm St. </street>
<city>Elko, NV</city>
<zipcode>85039</zipcode>
</shipto>}
Структура запроса
namespace xyz
= "http://www.xyz.com/example/names"
default element namespace
= "http://www.xyz.com/example/names"
default function namespace
= "http://www.xyz.com/example/functions"
schema namespace xhtml
= "http://www.w3.org/1999/xhtml"
at "http://www.w3.org/1999/xhtml/xhtml.xsd"
Выводы
Литература