• Спонсоры

  • Рубрики

  • Архивы

  • Популярное



Поиск в XML с помощью XQuery

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

Но это не мешает никому ее совершенствовать, т.к. она распространяется с открытым исходным кодом (правда, почему-то никто этого не делает — наверное всем достаточно текущих ее возможностей).

Почему в названии слово Lite? Потому что это сильно облегченный вариант языка XQuery, но Lite вполне достаточно для решения большинства задач по поиску и извлечению информации из XML. Официальная документация по XQuery находится здесь.

Итак, библиотека XQuery Lite 1.0 была написана неким Luis Argerich (lrargerich@yahoo.com). В ее кратком описании значится следующее:

«This is an implementation of a subset of the Xquery language with intention to add new features in next releases. It is based on flwr expressions».

Если перевести, то получится что-то вроде «Это реализация подмножества языка XQuery с намерениями дальнейшего расширения ее возможностей. Базируется на FLWR-выражениях».

Что такое FLWR-выражения будет понятно дальше.

Итак, XQuery — это SQL-подобный язык. Т.е. поисковые запросы к базе данных можно составлять словами, в отличие от XPath. Сразу приведу пример:

test.xml

<?xml version="1.0"?>
<bib>
    <book year="1994">
        <title>TCP/IP Illustrated</title>
        <author>Someone1</author>
    </book>
    <book year="1990">
        <title>MS-DOS Operation System</title>
        <author>Someone2</author>
    </book>
</bib>

Допустим, нам нужно найти все книги в файле test.xml, с годом издания больше 1991:

test.php

<?
header('Content-type: text/plain;');

// подключаем класс XQuery Lite
include_once("class_xquery_lite.php");

// формируем запрос
$query = '<bib> {
    for $b in document("test.xml")/bib/book
    where $b/@year > 1991
    return
    <book year="{$b/@year}">
        {$b/title}
    </book>
}
</bib> '
;

$xq = new XqueryLite();
$result = $xq->evaluate_xqueryl($query);

// выводим результат
echo $result;
?>

Этот скрипт в итоге выведет:

&lt;bib&gt;<br />
&nbsp;&lt;book year="1994"&gt;<br />
&nbsp;&lt;title&gt;TCP/IP Illustrated&lt;/title&gt;<br />
&nbsp;&lt;/book&gt;<br />
&lt;/bib&gt;

Т.е., как видим, результатом будет не какой-то объект, как в случае DOM или SimpleXML, а текстовое представление, т.е. тот же XML, который в итоге можно будет дальше обработать уже другим способом.

В нашем запросе мы использовали конструкцию типа FWR — FOR, WHERE, RETURN. Конструкция FLWR — это FOR, LET, WHERE, RETURN. Что такое LET?

LET — это конструкция присваивания. Рассмотрим немного измененный запрос:

$query = '&lt;bib&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;for $b in document("test.xml")/bib/book<br />
&nbsp;&nbsp;&nbsp;&nbsp;let $title := $b/title<br />
&nbsp;&nbsp;&nbsp;&nbsp;where $b/@year &gt; 1991<br />
&nbsp;&nbsp;&nbsp;&nbsp;return<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;book year="{$b/@year}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{$title}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt;<br />
}<br />
&lt;/bib&gt;';

Этот запрос вернет тот же результат. Как видим, конструкция {$title} возвращает содержимое тега title вместе с самим тегом. Чтобы отбросить теги, нужно использовать функцию text():

$query = '&lt;bib&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;for $b in document("test.xml")/bib/book<br />
&nbsp;&nbsp;&nbsp;&nbsp;let $title := $b/title/text()<br />
&nbsp;&nbsp;&nbsp;&nbsp;where $b/@year &gt; 1991<br />
&nbsp;&nbsp;&nbsp;&nbsp;return<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;book year="{$b/@year}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{$title}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt;<br />
}<br />
&lt;/bib&gt;';

Этот запрос вернет:

&lt;bib&gt;<br />
&nbsp;&lt;book year="1994"&gt;<br />
&nbsp;TCP/IP Illustrated<br />
&nbsp;&lt;/book&gt;<br />
&lt;/bib&gt;

Т.е. тег title отброшен, но оставлено его текстовое содержимое.

В полной версии языка XQuery есть конструкция FLOWR — с добавлением ORDER — сортировки. Но здесь мы рассматривать ее не будем, т.к. нашей библиотекой XQuery Lite оно не поддерживается.

Если Вы внимательно прочли запросы выше, то должны были заметить, что синтаксис некоторых частей совпадает с XPath:

/bib/book
$b/@year

Конструкция

for $b in document(«test.xml»)/bib/book

Означает, что мы переменной $b присвоим все содержимое элемента book, родителем которого является bib. Используя функцию document мы говорим интерпретатору, что база данных лежит во внешнем файле test.xml. Кстати, вместо document можно использовать xmlmem:

<?


// присваиваем содержимое xml переменной
$test_xml = '<?xml version="1.0"?>
<bib>
    <book year="1994">
        <title>TCP/IP Illustrated</title>
        <author>Someone1</author>
    </book>
    <book year="1990">
        <title>MS-DOS Operation System</title>
        <author>Someone2</author>
    </book>
</bib>'
;

// формируем запрос
$query = '<bib> {
    for $b in xmlmem($test_xml)/bib/book, $t in $b/title
    where $b/@year > 1991
    return
    <book year="{$b/@year}">
        {$t}
    </book>
}
</bib> '
;


?>

В этом случае интерпретатор возьмет содержимое XML из памяти — из текстовой переменной с именем $test_xml. Заметили, что я тут изменил запрос?

Этим я хотел показать, что в конструкции FOR можно перечислять присваивание переменных через IN, чтобы эти переменные потом использовать.

После ключевого слова WHERE (кстати, конструкция WHERE необязательна) следуют условия, по которым должна быть извлечена информация. Условий может быть несколько и они могут быть соединены логическими И/ИЛИ. Так, запрос

$query = '&lt;bib&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;for $b in xmlmem($test_xml)/bib/book, $t in $b/title<br />
&nbsp;&nbsp;&nbsp;&nbsp;where $b/@year &gt; 1989 and $b/@year &lt; 1994<br />
&nbsp;&nbsp;&nbsp;&nbsp;return<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;book year="{$b/@year}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{$t}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt;<br />
}<br />
&lt;/bib&gt;';

вернет

&lt;bib&gt;<br />
&nbsp;&lt;book year="1990"&gt;<br />
&nbsp;&lt;title&gt;MS-DOS Operation System&lt;/title&gt;<br />
&nbsp;&lt;/book&gt;<br />
&lt;/bib&gt;

Еще мы в WHERE можем использовать арифметические операции +,-,*,/. Можем использовать скобки для арифметических и логических выражений. Можем использовать функцию count() для вычисления количества элементов:

$test_xml = '&lt;?xml version="1.0"?&gt;<br />
&lt;bib&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;book year="1994"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;TCP/IP Illustrated&lt;/title&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;Someone1&lt;/author&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;book year="1990"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;MS-DOS Operation System&lt;/title&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;Someone2&lt;/author&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;Someone3&lt;/author&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt;<br />
&lt;/bib&gt;';<br />
<br />
$query = '&lt;bib&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;for $b in xmlmem($test_xml)/bib/book, $t in $b/title<br />
&nbsp;&nbsp;&nbsp;&nbsp;where $b/@year &gt; 1989<br />
&nbsp;&nbsp;&nbsp;&nbsp;return<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;book year="{$b/@year}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{$t}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt;<br />
}<br />
&lt;/bib&gt;';

Выполнение этого запроса вернет нам

&lt;bib&gt;<br />
&nbsp;&lt;book year="1994"&gt;<br />
&nbsp;&lt;title&gt;TCP/IP Illustrated&lt;/title&gt;<br />
&nbsp;&lt;/book&gt;<br />
&nbsp;&lt;book year="1990"&gt;<br />
&nbsp;&lt;title&gt;MS-DOS Operation System&lt;/title&gt;<br />
&nbsp;&lt;/book&gt;<br />
&lt;/bib&gt;

А выполнение этого

$query = '&lt;bib&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;for $b in xmlmem($test_xml)/bib/book, $t in $b/title<br />
&nbsp;&nbsp;&nbsp;&nbsp;where $b/@year &gt; 1989 and count($b/author) = 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;return<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;book year="{$b/@year}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{$t}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt;<br />
}<br />
&lt;/bib&gt;';

вернет

&lt;bib&gt;<br />
&nbsp;&lt;book year="1994"&gt;<br />
&nbsp;&lt;title&gt;TCP/IP Illustrated&lt;/title&gt;<br />
&nbsp;&lt;/book&gt;<br />
&lt;/bib&gt;

А еще мы можем выполнять запрос в запросе:

test.xml

<?xml version="1.0"?>
<bib>
    <book year="1994">
        <title>TCP/IP Illustrated</title>
        <author>Someone</author>
    </book>
    <book year="1990">
        <title>MS-DOS Operation System</title>
        <author>Someone</author>
    </book>
    <book year="2000">
        <title>Windows 2000 Operation System</title>
        <author>Microsoft</author>
    </book>
</bib>

test.php

<?

$query = '{
    for $d in document("test.xml")/bib/book, $a in distinct-values($d/author)
    return
    {
        for $b in document("test.xml")/bib/book, $b2 in $b/author
        where $b2 = $a and $b/@year != $d/@year
        return {$b}
    }
}'
;

?>

Такой запрос вернул у меня

&lt;book year="1990"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;MS-DOS Operation System&lt;/title&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;Someone&lt;/author&gt;<br />
&lt;/book&gt;<br />
&lt;book year="1994"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;TCP/IP Illustrated&lt;/title&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;Someone&lt;/author&gt;<br />
&lt;/book&gt;

Основные возможности я рассмотрел. Остальные Вы можете рассмотреть самостоятельно в справке к этому языку, которую я выложил в архиве. Еще в этом архиве Вы найдете сам класс XQuery Lite 1.0 (для версий php4/5) с примером его использования. Изначально класс был написан в 2002 году для тогдашней версии PHP 4 (а может даже 3). Пришлось его немного исправить, чтобы работал и в PHP5. Для версии 4 я не проверял, поэтому там могут быть ошибки. Но они легко устранимы, если посидеть пару часов 🙂

Того, что было рассмотрено в этой статье, Вам должно хватить для самостоятельного освоения этого весьма удобного языка для извлечения инфы из XML.

Я буду рад всем комментариям в блоге, а особенно тем, которые мне скажут, что кроме этого класса есть еще какие-нибудь методы для работы с XQuery в PHP 🙂





Читайте также:



3 комментария на “Поиск в XML с помощью XQuery”

  1. Evengard

    Класс не работает под ПХП 5 (((

  2. novice

    Какую ошибку выдает?

  3. Андрей

    Call to undefined function xmldoc() in … on line 207

    xmldoc — она вроде как только в 4-ке была

Оставить комментарий


© 2008 - 2017 i-novice.net | Все права защищены.