xmlhack.ru logo
>> статьи на xmlhack.ru

XQuery и абстракция данных

Автор: Курт Кейгл
Перевод: Екатерина Куликова
Опубликовано на XML.com (12.07.2007, англ.): http://www.xml.com/pub/a/2007/07/12/xquery-and-data-abstraction.html
Опубликовано на xmlhack.ru (12.12.2007, рус.): http://xmlhack.ru/texts/07/xquery_abstraction/xquery_abstraction.html
В закладки:   Del.icio.us   reddit

Представьте на секунду, что мы проходим всю компьютерную революцию заново, полагая, что вы уже знаете о таких вещах, как формирование языков программирования, развитие баз данных, рост сетей и Web и т. д. Например, хотя сети впервые сформировалась в 60-х годах, они не были такими популярными до 80-х, когда протокол Ethernet помог объединению рынка сетей, что позволило создать новый слой абстракции, названной TCP/IP, которая в свою очередь сделала Интернет жизнеспособным. Тем временем математика баз данных Теда Кодда (Ted Codd) на несколько лет раньше заложила основу для реляционных баз данных, что привело к принятию спецификации SQL в конце 80-х.

Однако что бы произошло, если бы парадигма распределённого программирования и основы для REST возникли бы до распространения SQL, и XML оказался бы создан раньше? Есть, безусловно, несколько недостатков этого предположения, не последним из которых является то, что XSD принял за отправную точку те же алгебры, что и SQL пару десятилетий назад, но давайте пока закроем глаза на такие анахронизмы.

Структурированный Язык Запросов (SQL) возник из-за очень конкретных потребностей. Реляционная модель данных явилась значительным шагом вперёд в разработке баз данных, потому что впервые дала возможность последовательно и целенаправленно создавать нелинейные базы данных. Однако когда все владельцы венчурных капиталов, стали активно работать с базами данных, риск разделения рынка стал настолько велик, что поставщики поняли: для них будет выгоднее установить официальный стандарт (SQL 87). Тогда если какой-то программист знал SQL, то он мог научиться программировать базы данных DB без необходимости тратить год на учебных курсах. Иными словами, одним из первых значительных преимуществ SQL как технологии было обеспечение уровня абстракции баз данных для всего множества реляционных баз данных, используемых в данный момент.

Абстракция баз данных является мощной концепцией, которую сегодня многие программисты принимают как должное. Идея о том, что вам не нужно ничего знать о структуре ваших баз данных означает несколько вещей:

В своё время технология SQL была несомненным успехом. Но она не остановила развитие баз данных (хотя в интересах поставщиков, разумеется, держать разработчиков в рамках своего конкретного продукта, по крайней мере на короткое время). Этой технологии удалось создать почти универсальный стандарт для работы с реляционными базами данных, что помогло реляционным базам данных стать наиболее предпочтительной формой хранения данных на следующие два десятилетия.

Однако, несмотря на то, что SQL базы данных стали нормой, возникло понимание, что есть и другие способы описания данных, каждый из которых в большей степени подходит для конкретных областей данных.Для высокоскоростного доступа к контенту со стабильной структурой и периодическими небольшими обновлениями, LDAP будет работать значительно быстрее, чем стандартный РСУБД. Для крупномасштабного анализа больших наборов данных, кубы OLAP работают лучше, чем модель РСУБД, и, хотя SQL часто используется с OLAP-кубами, он всё же не оптимизирован для этого. Различные устройства и даже файловые системы могут рассматриваться как базы данных, содержащие системную информацию, так как большинство современных файловых систем (такие как ext3 или Reiser) включают сложное журналирование, что представляется более похожим на базу данных, чем на традиционные файловые системы.

Наконец, произошёл взрыв XML-технологии и возникновение баз данных, основанных на XML. XML-база данных отличается от реляционной по нескольким ключевым параметрам:

В целом, XML базы данных обходят эти ограничения, используя имена каждого элемента в данном документе как названия таблиц, а отношения хранятся как указатели на дочерние элементы, атрибуты, текст и, возможно, родительские узлы. Индексация каждого элемента с учётом его текстового содержания позволяет осуществлять поиск с учётом близости между словами и извлекать узлы, являющиеся ближайшими предками для узла, содержащего данный текст. В некоторых случаях они также оптимизируют этот процесс, работая со схемами, связанными с различными XML-документами; в других случаях используют иные алгоритмы индексации, но в целом результат таков, что хотя очень легко восстановить XML из баз данных, информация хранится ни в последовательном формате, ни в моделях реляционных таблиц, как это принято в SQL.

Это имеет некоторые интересные последствия. Прежде всего, почти невозможно задать запрос SQL для XML, потому что SQL, вообще говоря, почти всегда (кроме простейших случаев) полагается на наличие чётко определённых первичных и внешних ключей, чтобы получить набор записей, а их по большей части нет в XML. Осознание этого пришло ещё в конце 90-х годов, что и послужило одним из стимулирующих факторов для развития XPath; это был способ произвести операции выборки из набора узлов XML-дерева.

Сейчас же XPath не любим во многих кругах. Отчасти причиной является то, что это не SQL. XPath имеет значительно более компактный синтаксис, он использует понятие контекста, которого фактически нет в SQL, и мир в нём не рушится от операций JOIN, что и не удивительно, так как XML не полагается на наличие пар типа первичный-внешний ключ. Web-разработчикам (и, к сожалению, ключевым людям в мире HTML спецификаций) это не нравится, потому что он довольно сложен и имеет синтаксис, аналогичный, но не совсем такой же, как и селекотры CSS. Хотя реализация Microsoft начиналась с достаточно простого набора методов XPath для документов и объектов элементов (selectNodes() и selectSingleNode()), в последние годы Microsoft и Mozilla создали значительно более сложные XPath-объекты, трудные и даже болезненные в написании, в основном, потому что они были реализованы в W3C DOM документации.

Несмотря на всё это, XPath всё ещё (вполне обоснованно) остаётся основным языком для выборки узлов для XSLT и конкретных DOM-операций, не говоря уже о том, что W3C предпринял серьёзные усилия для продвижения в разработку спецификации XPath 2.0 и спецификации XQuery в том числе. Действительно, когда вы погружаетесь в XQuery, его можно рассматривать как XPath 2.0 с оболочкой для присвоения переменных, контролем и условными предложениями, функциями и декларацией модулей, и несколько расширенным синтаксисом.

Почему XPath 2.0? Если XPath 1.0 уже настолько утвердился, для чего же требуется такое довольно значительное обновление? Ответ снова возвращается к большой проблеме: что же представляют из себя данные. XPath 1.0 делает упор на XML структуру. Это означает, что XPath может манипулировать объектами (или коллекциями объектов), например, строками, числами и другими типами, только если они описаны как текстовые узлы. Оказалось, что это значительное ограничение для XSLT (и одна из причин того, что функция node-set() является одним из наиболее универсальных расширений для реализаций XSLT 1.0), а также означает, что выражения XPath 1.0 могут работать одновременно только с одним документом.

XPath 2.0, с другой стороны, вводит четыре новых понятия, которые довольно радикально изменяют то, что вы можете сделать с языком.

Последовательности

Во-первых, последовательность — это список, содержащий всё, что угодно. Это может быть список узлов, номеров, строк, дат и различных сочетаний этих типов. Имея такую структуру, вы можете создавать итераторы и счётчики, выполнять операции с наборами объектов, и, следовательно, строить группы узлов «на лету» без необходимости прибегать к функциям расширений.

Ниже приводятся характерные примеры последовательностей:

(3,5,8,10)
("Monday","Tuesday","Wednesday","Thursday","Friday")
(3 to 6)  -- Same as (3,4,5,6)
(//p,//h1,//h2)
(3,4,"abc",//li)

Схемы основных типов

Второе изменение — это официальное представление примитивных схем типов данных в языке. Это позволяет и более точно определить поведение расширений XPath (а также XSLT и XQuery), и облегчить процессорам XPath и XQuery оптимизацию поиска. Хотя есть некоторые минусы интеграции типизированного контента, по большей части это дополнение предоставит вам более удобные и безопасные типы запросов и преобразований. Чуть позже мы рассмотрим использование типов данных более формально.

Условный и итеративный операторы

Третье изменение — добавление условных и итеративных операторов в XPath. Например, следующее выражение XPath вернёт последовательность, состоящую из длины всех абзацев документа (настоящей статьи), которые содержат более 300 символов:

for $para in //p return 
    if(string-length($para)>800) then string-length($para) else ( )

Это должно открыть глаза на несколько особенностей: результирующий набор — это не набор XML узлов, а последовательность чисел, и вы можете создать временные переменные в этом выражении XPath, которые сделают его понятнее. В нашем случае в переменной $paraбудет содержаться каждый абзац этого документа (в определённый момент в цикле). Заметим, что если фильтрующее выражение (условие if()) не соблюдено, то в результате получится пустая последовательность и она удаляется из списка, т.е.

(a,b,(c,d),e,( ),f)

становится

(a,b,c,d,e,f)

Хотя в XPath до сих пор имеется запрет на создание новых узлов (эта роль отдана XQuery, XSLT, или всему, что является оболочкой движка XPath), вы можете создавать довольно сложные наборы результатов, используя уже имеющиеся операторы XPath.

Расширения XPath

Окончательное и существенное изменение заключается в создании чётко определённого механизма расширений для XPath. На практике это означает, что для любых движков XPath 2.0, если вы сохраняете соответствие с механизмом объявления методов, вы можете создавать расширения XPath на Java, JavaScript, PHP,. NET и на других традиционных языках. Также вы можете создавать функции-расширения XPath, используя XSLT и XQuery. Оба эти инструмента дают радикальные средства для создания сложных API, и в то же время остаются ориентированными на XML.

Создав общий механизм интерфейсов, группа XPath 2.0 официально санкционировала идею создания расширений и сделала это таким образом, что даже если вы перешли с одной платформы или языка программирования на другую, то все изменения будут связаны лишь с повторной реализацией расширения на новом языке; а код XQuery или XSLT остаётся прежним.

Оболочка организует расширения XPath в пространства имён, и доступ к ним происходит через через префиксы этих пространств имён. Например, предположим, что вам необходимо определить функцию (to-title-case()), которая будет принимать выражение вроде «Это тест» и превращать его в строковую переменную с заглавными буквами без пробелов «ЭтоТест» (полезная подготовка для превращения названий в имена элементов). В XSLT 2.0, такая функция будет определяться следующим образом:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:str="http://www.metaphoricalweb.org/xmlns/string-utilities"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    version="2.0">
    <xsl:function name="str:title-case" as="xs:string">
        <xsl:param name="expr"/>
        <xsl:variable name="tokens" select="tokenize($expr,' ')"/>
        <xsl:variable name="titledTokens" select="for $token in $tokens return
            concat(upper-case(substring($token,1,1)),
                      lower-case(substring($token,2)))"/>
        <xsl:value-of select="string-join($titledTokens,'')"/>
    </xsl:function>
    <xsl:template match="/">
        <data><xsl:value-of select="str:title-case('This is a test')"/></data>
    </xsl:template>
</xsl:stylesheet>

В XQuery, это определение имеет чуть более компактную, но сходную структуру:

declare namespace str="http://www.metaphoricalweb.org/xmlns/string-utilities";
declare namespace xs="http://www.w3.org/2001/XMLSchema";

declare function str:title-case($expr as xs:string) as xs:string {
    let $tokens := tokenize($expr,' ')
    let $titledTokens := for $token in $tokens return
          concat(upper-case(substring($token,1,1)),lower-case(substring($token,2)))
    return string-join($titledTokens,'')
    };
    
<data>{str:title-case("This is a test")}</data>

В обоих случаях сначала объявлено пространство имён для функции. Здесь оно представлено так:

xmlns:str="http://www.metaphoricalweb.org/xmlns/string-utilities"

Для входных параметров и результирующей стоимости продукции заданы типы данных с использованием оператора и атрибута as. Конкретные объявления функции могут различными в разных языках, но в целом сигнатура должна оставаться одинаковой для всех платформ. Наконец, в обоих случаях ссылки на вызов функции и передаваемые параметры одинаковы, потому что в том месте, где функции вызываются, они по существу являются функциями XPath 2.0:

XSLT: <data><xsl:value-of select="str:title-case('This is a test')"/></data>
XQuery: <data>{str:title-case("This is a test")}</data>

Заметим, что этот принцип остаётся верным независимо от принимающего языка; Java-класс, который вызывается из Saxon’а (или Xalan’а) по-прежнему будут объявлен как пространство имён, кроме только того, что в данном случае будет пространство имён, соответствующее Java-классу определено в пути к классу, а протокол будет называться java:.

xmlns:str = "java:org.metaphoricalweb.stringUtilities"

Для .NET и PHP 5 таже существуют аналогичные механизмы, позволяющие связать функции (или классы функций) в этих языках, с соответствующим пространством имён.

Абстракция XQuery и объектно-ориентированное программирование

Во многих отношениях этот механизм расширений является одним из наиболее важных аспектов XPath 2.0. Обычно вы связываете не одну единственную функцию с пространством имён, а целый ряд схожих функций. Например, такая функция как str:from-title-case(), которая выполняет обратную операцию к str:title-case() то есть преобразование строки из непрерывной с большими буквами в строку, разделённую пробелами, вполне может быть частью класса строковых утилит, как и сама title-case(). Эта идея используется довольно широко в базах данных XML, таких как eXist для предоставления механизмов доступа в базу данных из контекста XQuery-кода, и с точки зрения дизайна переводит развёртывание XQuery-утверждений скорее в объектно-ориентированный подход, чем в простые процедурные скрипты.

Эта возможность как расширить XPath, так и создать объектный механизм манипулирования, который в XPath повторяет концепцию хранимых процедур SQL, причём основная разница в том, что большинство реализаций хранимых процедур не имеют понятия о пространствах имён или классах. Однако последствия, которые вытекают из разделения пространств имён вовсе не являются незначительными.

XML долго сопротивлялся тому, чтобы соответствовать объектно ориентированной парадигме, поскольку XML-структура трудно инкапсулируется и, вообще говоря, не имеет функциональных методов, связанных с ней (с другой стороны, полиморфизм он стал поддерживать прекрасно). С другой стороны, если у вас есть функциональное пространство имён в языке, таком как XQuery, то тогда переход к объектно-ориентированному программированию сводится лишь к способности создать уникальный ключ для получения виртуальной записи в некоторой форме, а затем применение этого ключа в качестве первого аргумента в любой функции XQuery из этого пространства имён.

Например, предположим, что у вас есть набор записей счётов собранный в XML формате и пространство имён счётов, названное соответственно invoice:. В этом случае вы можете определить метод invoice:new(args), который будет создавать новую запись счёта, и, что важно, возвращать уникальный идентификатор GUID этой записи:

let $invoice := invoice:new()
return $invoice
-- "A1DFCDEA22318165226AB10DAA411442D439"

Как только вы это сделали, данный ключ становится параметром для любого другого метода в рамках нашего просртанства имён:

invoice:addItemX($invoice,
     <item>
          <name>Pencil</name>
          <quantity>12</quantity>
          <sku>C11D-A</sku>
          <cost>0.89</cost>
     </item>
     )
(: or :)
invoice:addItem($invoice,"Pencil",12,"C11d-A",0.89)
invoice:getObject($invoice)

Иными словами, функциональность XPath 2.0 (через XQuery или XSLT) даёт возможность работы с объектами, в тех случаях, когда данные не включены в объект, а хранятся во внешних источниках или даже существуют только в виртуальной форме.

Абстракция данных — это процесс, в котором вы предоставляете доступ и механизмы обновления для данной базы данных (независимые от имплементации), прозрачные для принимающей системы. SQL до определённой степени берёт этот процесс на себя, но многие основополагающие моменты объектно-ориентированного программирования так не получили в нём поддержки, пока SQL не был стандартизирован. Это означало, что большинство приложений выглядело как систематическое объединение взгляда SQL и объектно-ориентированного взгляда на устройство Вселенной, как правило, за счёт крайней связанности. Большинство оболочек стали поддерживать некоторые хитрости для синхронизации между этими двумя взглядами на мир, но они, как правило, налагают достаточно большие ограничения на содержимое SQL кода.

XPath 2.0 и XQuery с другой стороны, имеют потенциал абстрагироваться не только от одной реализации базы данных SQL, но и от самого SQL. Если вы можете создать XML-мост и к запросам, и к обновлениям определённого вида хранилища данных (того, где XML создаётся или принимается, даже если основные операции имеют дело с данными не в виде XML), то тогда вы можете использовать XQuery, чтобы манипулировать XML-стороной моста и построить слой абстракциии объектов. Таким образом, LDAP-взаимодействия, текстовые файлы, OLAP-кубы, JSON, а также другие протоколы данных можно абстрагировать с помощью объектного слоя XQuery так, что их различия становятся невидимыми для процессов, которые используют эти сервисы.

Заметьте, это не означает, что LDAP будет реализован в XML, или что JSON не планирует создать отображение в XML (хотя взгляните на E4X), или что технология SQL уйдёт в ближайшее время. Очевидно, есть ситуации, когда специфические возможности этих систем требуют, чтобы всё было сделано в форме, наиболее оптимизированной для этих сред. Однако удивительно велико число случаев, когда важно то, что данные будут доступны самым прозрачным образом для всего процесса разработки, независимо от того, где они хранятся. В этих случаях, XQuery кажется мне наилучшим выходом.

Выводы

Как нелегко описать все аспекты SQL в одной статье, так же трудно рассказать о том, что даёт XQuery для абстракции данных и распределённого объектно-ориентированного программирования. В следующей части этой серии публикаций, я собираюсь рассмотреть основные механизмы работы XQuery; после этого будут описаны существующие на настоящий момент реализации XQuery в современных XML базах данных и связанных с ними системах.

Сначала я скептически относился к XQuery, когда начинал с ним работать. Теперь, когда я вижу возможности, которые открываются в связи с модуляризацией языка, появлением всё новых хранилищ данных, доступных для XQuery, и переходом к сложным клиентскими приложениями, которые всё более нацелены на XML, я признаю, что ошибался в моих первоначальных оценках. XQuery — это очень важная технология, которая, весьма вероятно, сильно повлияет на то, как будут построены приложения, особенно в распределённых средах.

Курт Кейгл (Kurt Cagle), разработчик систем и информационный архитектор, живущий в Виктории, Британская Колумбия. Курт также является web-мастером сайта XForms.org, портала новостей и программного кода для сообщества XForms и XQuery.



XML.com Copyright © 1998-2007 O'Reilly Media, Inc.
Перевод: xmlhack.ru Copyright © 2000-2007 xmlhack.ru