Лабораторная работа №3 Цель работы: Получение практических навыков по работе с Apache Solr Задачи работы: Подготовка окружения для работы с Apache Solr Развертывание Apache Blaze Индексирование тестовой коллекции Преобразование результатов поиска из XML в HTML с использованием XSLT 1.1 Общая информация об Apache Lucene, Solr и проекте Blaze The Apache Lucene — это свободная библиотека для высокоскоростного полнотекстового поиска, написанная на Java. Может быть использована для поиска в интернете и других областях компьютерной лингвистики (аналитическая философия). Основные возможности: Масштабируемая и высокоскоростная индексация o свыше 95GB в час на современном оборудовании o требуется малый объем RAM — «heap» всего 1MB o размер индекса примерно 20-30 % от размера исходного текста Мощный, точный и эффективный поисковый алгоритм o ранжированный поиск — лучшие результаты показываются первыми o множество мощных типов запросов: запрос фразы, wildcard запросы, поиск интервалов и т. д. o поиск основанный на «полях» (таких как, заголовок, автор, текст) o возможность сортировать по различным полям o multiple-index поиск с возможностью объединения результатов o возможность одновременного поиска и обновления индекса Кроссплатформное решение o исходный код полностью написан на Java o наличие портов на другие языки программирования Apache Solr – открытая корпоративная поисковая платформа, созданная на базе Apache Lucene. Основные возможности Apache Solr включают в себя мощный полнотекстовый поиск, подсветку результатов поиска, фасетный поиск, динамическую кластеризацию результатов, интеграцию с СУБД и поддержку индексирования документов разных форматов (например, MS Word и PDF). Также имеется возможность создания распределенной поисковой системы и репликация поискового индекса. Проект Apache Solr написан на Java и запускается как самостоятельное приложение в контейнере сервлетов, например, на Apache Tomcat. Solr использует Lucene как поисковый механизм и имеет REST HTTP/XML и JSON интерфейс для взаимодействия с другими приложениями. Blaze – отдельный проект, который представляет собой готовый к работе виртуальный сервер с уже установленным и настроенным Apache Solr. Blaze также не является готовым решением для организации корпоративного поиска, но позволяет сосредоточиться на создании системы корпоративного поиска, вместо установки и настройки Apache Lucene. 1.2 Подготовка окружения Для начала работы с Blaze его необходимо подключить как новую виртуальную машину к VMWare Player. Данная операция выполнялась в первой лабораторной работе, в случае возникновения проблем обратитесь к ней. Blaze представляет собой дистрибутив OpenSUSE Linux с установленным и настроенным Apache Solr. После загрузки инсталляции мы видим интерфейс командной строки. Для входа логин root, пароль linux. Для того, чтобы получить доступ с хост-машины к виртуальной необходимо изменить режим эмуляции сетевого адаптера с Bridged (по-умолчанию для данной виртуальной машины) в режим NAT. Для этого в настройках виртуальной машины, в разделе Network Adapters указываем NAT вместо Bridged. Запускаем виртуальную машину, в процессе загрузки нам будет показан адрес, по которому можно получить доступ к панели управления SUSE Linux. В данном примере это адрес https://192.168.81.129:54984/ Чтобы получить доступ к Solr нужно перейти по адресу http://ip-адрес/solr/admin/, в данном случае http://192.168.81.129/solr/admin/ Веб-интерфейс Solr Все результаты, возвращаемые Solr представлены в формате XML. Это необходимо для успешной интеграции с другими системами, так как XML является в данном случае наиболее преемлемым форматом. 1.3 Добавление документов в поисковый индекс Solr запущен, но на данный момент он не содержит каких-либо данных в поисковом индексе. Можно добавить документы в индекс путем отправки Solr инструкций в формате XML на добавление (обновление), удаление документов, подтверждение операции отложенного добавления или удаления, обновления индекса. В общем случае, для того, чтобы обновить документ в индексе нужно на адрес /solr/update отправить XML-документ. Для отправки данный на сервер воспользуется php скриптом, который будет брать содержимое XML файлов из текущей директории и отправлять их Solr, а затем делать подтверждение внесенных изменений: <?php echo "POSTing data to solr<br />"; $url = "http://192.168.81.129/solr/update"; $files = scandir("."); foreach ($files as $file) { if (strpos($file, ".xml") !== false) { echo "Processing file ".$file."<br />"; $tCurl = curl_init(); curl_setopt($tCurl, CURLOPT_URL, $url); curl_setopt($tCurl, CURLOPT_HTTPHEADER, array( 'Content-type: text/xml' )); curl_setopt($tCurl, CURLOPT_HEADER, 0); $data = file_get_contents($file); curl_setopt($tCurl, CURLOPT_POSTFIELDS, $data); curl_exec($tCurl); $tCurl = curl_init(); curl_setopt($tCurl, CURLOPT_URL, $url); curl_setopt($tCurl, CURLOPT_HTTPHEADER, array( 'Content-type: text/xml' )); curl_setopt($tCurl, CURLOPT_HEADER, 0); $data = "<commit/>"; curl_setopt($tCurl, CURLOPT_POSTFIELDS, $data); curl_exec($tCurl); } } ?> Мы можем поместить данный скрипт в директорию с тестовыми XML-файлами для отправки и выполнить через интерфейс командной строки, например, так: C:\>d:\WebServer\usr\local\php5\php-cgi.exe d:\WebServer\home\test\www\index.php Или открыв в браузере страницу, которая вызовет данный скрипт. В результате мы добавили в Solr документы. Solr, в отличие от других систем хранит не документ целиком и выполняет поиск по нему, а разбивает XML-документ на поля и индексирует каждое из них. Для проверки выполним запрос по ключевому слову Dell и в результате мы получим также ответ от сервера в формате XML: <response> <lst name="responseHeader"> <int name="status">0</int> <int name="QTime">10</int> <lst name="params"> <str name="indent">on</str> <str name="start">0</str> <str name="q">Dell</str> <str name="rows">10</str> <str name="version">2.2</str> </lst> </lst> <result name="response" numFound="1" start="0"> <doc> <arr name="cat"> <str>electronics</str> <str>monitor</str> </arr> <arr name="features"> <str> 30" TFT active matrix LCD, 2560 x 1600, .25mm dot pitch, 700:1 contrast </str> </arr> <str name="id">3007WFP</str> <bool name="inStock">true</bool> <str name="includes">USB cable</str> <str name="manu">Dell, Inc.</str> <str name="name">Dell Widescreen UltraSharp 3007WFP</str> <int name="popularity">6</int> <float name="price">2199.0</float> <float name="weight">401.6</float> </doc> </result> </response> Как видим, результат не просто представляет собой текс, а набор полей из исходного XMLдокумента. 1.4 Формирование запросов Так как документ в поисковом индексе представляет собой набор полей, то возможно формировать сложные поисковые запросы, которые при выполнении используют значения отдельных полей документа. Примеры: 1. По содержанию значения в каком-либо поле документа Формат: значение Пример: video 2. По содержанию в поле определенного значения Формат: имяПоля:значение Пример: name:video 3. По значению поля, находящемуся в определенном интервале Формат: имяПоля:[a TO b] Пример: popularity:[0 TO 10] Возможно также использование маски *, то позволяет ограничивать интервал только с одной из сторон, например, [* TO 10] – значение определенного поля меньше 10. 4. По отсутствию какого-либо значения в поле Формат: -имяПоля:значение Пример: -popularity:10 Также возможно использование интервальных значений 5. По обязательному наличию поля или какого-либо его значения в документе Формат: +имяПоля Пример: +price 1.5 Расширенный синтаксис запросов Solr содержит также и расширенный интерфейс формирования запросов, который позволяет не только производить выборку, но и определять структуру результатов. Для перехода в расширенный интерфейс необходимо перейти по ссылке Full Interface на странице Solr. Оно обладает дополнительно следующими возможностями: 1. Разделить запрос на две части: запрос на выборку данных и условия запроса 2. Определить количество возвращаемых результатов 3. Определить перечень полей результата 4. Отладка запроса – просмотр последовательности формирования результата 5. Подсветка указанных полей результата 1.6 Взаимодействие Solr с другими системами, XSLT преобразование Solr возвращает результаты поиска в формате XML. Данный формат удобен для хранения и представления структурированных данных, но неудобен для восприятия и дальнейшей обработки. Для того чтобы стилизовать XML используется XSLT. XSLT (eXtensible Stylesheet Language Transformations) — язык преобразования XML-документов. Спецификация XSLT входит в состав XSL и является рекомендацией W3C. При применении таблицы стилей XSLT, состоящей из набора шаблонов, к XML-документу (исходное дерево) образуется конечное дерево, которое может быть сериализовано в виде XMLдокумента, XHTML-документа (только для XSLT 2.0), HTML-документа или простого текстового файла. Правила выбора (и, отчасти, преобразования) данных из исходного дерева пишутся на языке запросов XPath. XSLT имеет множество различных применений, в основном в области web-программирования и генерации отчётов. Одной из задач, решаемых языком XSLT, является отделение данных от их представления, как часть общей парадигмы MVC. Другой стандартной задачей является преобразование XML-документов из одной XML-схемы в другую. В процессе выполнения XSLT-преобразования задействованы: один или несколько входных XML-документов; одна или несколько таблиц cтилей XSLT; XSLT-процессор; один или несколько выходных документов. В простейшем случае XSLT-процессор получает на входе два документа — входной XML-документ и таблицу стилей XSLT — и создает на их основе выходной документ. Язык XSLT является декларативным, а не процедурным. Вместо определения последовательности исполняемых операторов, этот язык определяет правила, которые будут применяться во время преобразования. Само преобразование ведется по фиксированному алгоритму. В первую очередь XSLT процессор разбирает файл преобразования и строит XML дерево входного файла. Затем он ищет шаблон, который лучше всего подходит для корневого узла и вычисляет содержимое найденного шаблона. Инструкции в каждом шаблоне могут либо напрямую говорить XSLT процессору "создай здесь такой-то тег", либо говорить "обработай другие узлы по тому же правилу, что и корневой узел". Каждый XSLT-процессор должен выполнить следующие шаги для подготовки к трансформации: 1. Прочитать таблицу стилей XSLT с помощью XML-парсера и перевести его содержимое в дерево узлов (дерево таблицы стилей), согласно модели данных XPath. Синтаксические ошибки «времени компиляции» определяются на этой стадии. Таблицы стилей могут быть модульными, поэтому все включения (инструкции xsl:include, xsl:import) также будут обработаны на этой стадии с целью объединить все шаблонные правила и иные элементы из других таблиц стилей в единое дерево таблицы стилей. 2. Прочитать входные данные XML с помощью XML-парсера, перевести его содержимое в дерево узлов (исходное дерево), согласно модели данных XPath. XML-документ может ссылаться на другие XML-источники с помощью вызова функций document(). Эти вызовы обычно обрабатываются во время выполнения, так как их расположение может являться вычисляемым, а вызовы соответствующих функций могут не происходить вовсе. (Пример выше не ссылается на какие-либо другие документы.) 3. Удалить пустые узлы из таблицы стилей XSLT, кроме тех, которые являются потомками от элементов xsl:text. Это позволяет исключить появление «лишних» пробелов. 4. Удалить пустые текстовые узлы из исходного дерева, если инструкции xsl:strip-space присутствуют в исходном документе. Это позволяет исключить появление «лишних» пробелов. (Пример выше не использует эту возможность) 5. Пополнить XSLT-дерево тремя правилами, которые предоставляют поведение по умолчанию для любых типов узлов, которые могут быть встречены при обработке. Первое правило — для обработки корневого узла; оно даёт инструкцию процессору обработать каждого потомка корневого узла. Второе правило — для любых текстовых узлов или узлов атрибутов; он даёт команду процессору сделать копию этого узла в результирующем дереве. Третье правило — для всех узлов комментария и узлов-инструкций обработки; никакой операции не производится. Шаблоны, явно заданные в XSLT, могут перекрывать часть или все шаблоны-правила, заданные по умолчанию. Если шаблон не содержит явных правил, встроенные правила будут применены для рекурсивного обхода исходного дерева и только текстовые узлы будут скопированы в результирующее дерево (узлы атрибутов не будут достигнуты, так как они не являются «детьми» их родительских узлов). Полученный таким образом результат обычно нежелателен, так как он является просто конкатенацией всех текстовых фрагментов из исходного XML-документа. Затем процессор проделывает следующие шаги для получения и сериализации результирующего дерева: 1. Создаёт корневой узел результирующего дерева. 2. Обрабатывает корневой узел исходного дерева. Процедура обработки узла описана ниже. 3. Сериализует результирующее дерево, если необходимо, согласно подсказкам, описанным инструкцией xsl:output. При обработке узла производятся следующие действия: 1. Производится поиск наиболее подходящего шаблона правила. Это достигается проверкой соответствия шаблона (который является выражением XPath) для каждого правила, указывая узлы, для которых правило может быть применено. Каждому шаблону процессором назначается относительный приоритет и старшинство для облегчения разрешения конфликтов. Порядок шаблонных правил в таблице стилей также может помочь разрешению конфликтов между шаблонами, которые соответствуют одинаковым узлам, но это не оказывает влияния на порядок, в котором узлы будут обрабатываться. 2. Элементы в пространстве имён XSLT (обычно имеющие префикс xsl:) трактуются как инструкции и имеют специальную семантику, которая указывает на то, как они должны интерпретироваться. Одни предназначены для добавления узлов в результирующее дерево, другие являются управляющими конструкциями. Не XSLT-элементы и текстовые узлы, обнаруженные в правиле, копируются, «дословно», в результирующее дерево. Комментарии и управляющие инструкции игнорируются. Инструкция xsl:apply-templates при её обработке приводит к выборке и обработке нового набора узлов. Узлы идентифицируются с помощью выражения XPath. Все узлы обрабатываются в том порядке, в котором они содержатся в исходном документе. XSLT расширяет библиотеку функций XPath’s и позволяет определять XPath-переменные. Эти переменные имеют разную область видимости в таблице стилей, в зависимости от того, где они были определены и их значения могут задаваться за пределами таблицы стилей. Значения переменных не могут быть изменены во время обработки. Хотя эта процедура может показаться сложной, однако она делает XSLT по возможностям похожей на другие языки web-шаблонов. Если таблица стилей состоит из единственного правила, предназначенного для обработки корневого узла, в результат просто копируется всё содержимое шаблона, а XSLT-инструкции (элементы 'xsl:…') заменяются вычисляемым содержимым. XSLT предлагает даже специальный формат («literal result element as stylesheet») для таких простых, одношаблонных трансформаций. Однако, возможность определять отдельные шаблоны и правила сильно увеличивает гибкость и эффективность XSLT, особенно при генерации результата, который очень похож на исходный документ. Выполним в качестве примера преобразование одного из результатов поиска - сохраним один из результатов в XML-файл и для XSLT-преобразования воспользуемся следующим скриптом: <?php // Объект исходного XML-документа $xml = new DOMDocument("1.0", 'UTF-8'); $xml->load('select.xml'); // Объект стиля $xsl = new DOMDocument(null, 'UTF-8'); $xsl->load('xslt.xsl'); // Создание парсера $proc = new XSLTProcessor(); // Подключение стиля к парсеру $proc->importStylesheet($xsl); // Обработка парсером исходного XML-документа $parsed = $proc->transformToXml($xml); // Вывод результирующего кода echo $parsed; ?> Данный скрипт загружает XML-документ из файла select.xml и применяет к нему xsl-стиль из файла xslt.xsl. Листинг файла select.xml <?xml version="1.0" encoding="UTF-8"?> <response> <lst name="responseHeader"> <int name="status">0</int> <int name="QTime">3</int> <lst name="params"> <str name="explainOther"/> <str name="fl">*</str> <str name="indent">on</str> <str name="start">0</str> <str name="q">*</str> <str name="hl.fl"/> <str name="qt">standard</str> <str name="wt">standard</str> <str name="fq">-video +price:[200 TO *] +popularity:[3 TO 6]</str> <str name="rows">10</str> <str name="version">2.2</str> </lst> </lst> <result name="response" numFound="3" start="0"> <doc> <arr name="cat"> <str>electronics</str> <str>hard drive</str> </arr> <arr name="features"><str>SATA 3.0Gb/s, NCQ</str><str>8.5ms seek</str><str>16MB cache</str></arr> <str name="id">6H500F0</str> <bool name="inStock">true</bool> <str name="manu">Maxtor Corp.</str> <date name="manufacturedate_dt">2006-02-13T15:26:37Z</date> <str name="name">Maxtor DiamondMax 11 - hard drive - 500 GB - SATA300</str> <int name="popularity">6</int> <float name="price">350.0</float> </doc> <doc> <arr name="cat"><str>electronics</str><str>monitor</str></arr> <arr name="features"><str>30" TFT active matrix LCD, 2560 x 1600, .25mm dot pitch, 700:1 contrast</str></arr> <str name="id">3007WFP</str> <bool name="inStock">true</bool> <str name="includes">USB cable</str> <str name="manu">Dell, Inc.</str> <str name="name">Dell Widescreen UltraSharp 3007WFP</str> <int name="popularity">6</int> <float name="price">2199.0</float> <float name="weight">401.6</float> </doc> <doc> <arr name="cat"><str>electronics</str><str>monitor</str></arr> <arr name="features"><str>19" TFT active matrix LCD, 8ms response time, 1280 x 1024 native resolution</str></arr> <str name="id">VA902B</str> <bool name="inStock">true</bool> <str name="manu">ViewSonic Corp.</str> <str name="name">ViewSonic VA902B - flat panel display - TFT 19"</str> <int name="popularity">6</int> <float name="price">279.95</float> <float name="weight">190.4</float> </doc> </result> </response> Листинг xslt.xsl <?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml"> <xsl:output method="xml" indent="yes" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1strict.dtd"/> <xsl:template match="/"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>test1</title> <style type="text/css"> h1 { padding: 10px; padding-width: 100%; background-color: silver } td, th { width: 40%; border: 1px solid silver; padding: 10px } td:first-child, th:first-child { width: 20% } table { width: 650px } </style> </head> <body> <xsl:apply-templates/> </body> </html> </xsl:template> <xsl:template match="result/doc"> <h1> <xsl:value-of select="str[@name='name']"/> </h1> <table> <tr> <td>Popularity</td> <td><xsl:value-of select="int[@name='popularity']"/></td> </tr> <tr> <td>Price</td> <td><xsl:value-of select="float[@name='price']"/></td> </tr> </table> </xsl:template> <xsl:template match="lst"> </xsl:template> </xsl:stylesheet> В результате из XML-документа мы получаем HTML-документ с форматированием и оформлением: В приведенном выше примере используется два шаблона: первый – для формирования структуры html-документа, воторой для вывода каждого отдельного результата поиска: <xsl:template match="result/doc"> <h1> <xsl:value-of select="str[@name='name']"/> </h1> <table> <tr> <td>Popularity</td> <td><xsl:value-of select="int[@name='popularity']"/></td> </tr> <tr> <td>Price</td> <td><xsl:value-of select="float[@name='price']"/></td> </tr> </table> </xsl:template> Рассмотрим основные части шаблона: <xsl:template match="result/doc"> … </xsl:template> Данный блок определяет вывод узлов, которые попадают под шаблон match="result/doc", т.е. находятся внутри узла doc, который находится внутри узла result. <xsl:value-of select="str[@name='name']"/> Вывод значения узла str, который содержит атрибут name, значение которого равно name. Как видим, вывод значений из узлов xml-документа отделен от форматирования, что позволяет определить его отдельно. В данном случае, у нас определена html-таблица, в которую выводятся значений из двух узлов результата поиска. <table> <tr> <td>Popularity</td> <td></td> </tr> <tr> <td>Price</td> <td> </td> </tr> </table> 1.7 Задание для самостоятельного выполнения С использованием расширенного интерфейса запросов сформировать следующие запросы: 1. Выбрать документы, содержащие в названии (поле name) video и не находящиеся в категории music (поле категории – cat) 2. Из результата предыдущего запроса вывести только следующие поля: a. features b. name c. price 3. Выбрать документы, значение параметра стоимость (поле price) лежит в пределах от 100 до 200 и вывести в результаты только поля название и стоимость. 4. Вывести три любых документа из поискового индекса. 5. Вывести документы, значение параметра стоимость в которых больше 200, в документе нет упоминания слова video, популярность (поле popularity) лежит в пределах от 3 до 6. Вывести только название. 6. В XSL-шаблон добавить вывод еще трех полей: a. Дата производства (manufacturedate_dt) b. Наличие на складе (inStock) c. Категория (cat) 1.8 Отчет по лабораторной работе Отчет по лабораторной работе должен включать в себя: 1. Титульный лист, название, цель работы 2. Запросы и результаты запросов из задания для самостоятельного выполнения 3. Скорректированный XSL-шаблон и результаты XSLT-преобразования 4. Выводы о проделанной работе