Pull to refresh

Простой RDF-репозиторий на PHP и MySQL для начинающих

Reading time 6 min
Views 5.7K
Проблема: сложность практического освоения технологий семантической сети начинающими.

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

Задача: разобраться в создании, хранении и обращении к словарю, в использовании языка запросов на начальном уровне на практике.

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


Исходные условия: распространенная, доступная, понятная и документированная связка PHP+MySQL. Другое не предлагаем, хотя и знаем, что MySQL является не самым лучшим выбором для RDF-репозитория, так как сама идеология любой реляционной БД плохо подходит для работы с нежестко структурированными данными.

Мы не будем стараться писать всё с нуля, мы же начинающие и хотим, поняв общие принципы, попробовать что-то сделать да побыстрее. Также мы не стараемся здесь разъяснить основные термины, которые становятся ясны после прочтения нескольких первых попавшихся статей.

Единственным(?) внятным готовым решением для PHP+MySQL является ARC. Вся установка заключается в копировании папки и подключения скрипта с указанием данных доступа к БД:
include_once("semantic/arc/ARC2.php");

$config = array(
'db_name' => 'db',
'db_user' => 'user',
'db_pwd' => 'password',
'store_name' => 'arc',
);
$store = ARC2::getStore($config);


Следующий код создаст нужные для работы таблицы, если их нет:
if (!$store->isSetUp()) {
$store->setUp();
}


Теоретически движок есть. Он поддерживает хранение троек, распознавание троек в самых распространенных форматах, запросы на SPARQL и даже делает точку доступа.

RDF — это общее название всей модели, технологии представления данных в виде троек, или фактов, или более оффициально триплетов. Видов непосредcтвенно записи троек существует несколько: оригинальный RDF/XML, различные краткие нотации вроде N3 или просто изображение в виде графа. Так как помимо самих троек необходимо делать соглашения о смысле и взаимосвязях предикатов (типов ссылок или ребер графа от объекта к субъекту в тройке), то их описывают с помощью RDF Schema (RDFS).

Язык или скорее набор правил OWL предлагает запись всей онтологии с помощью классов, их отношений и конкретных объектов (индивидов). Опять же OWL можно выразить разными способами: тем же RDF/XML, OWL/XML, функциональным или «манчестерским» синтаксисом и т.д. Можно сказать, что OWL сомещает в себе запись троек RDF с их описанием RDFS на более высоком продуманном уровне, но и более сложном для освоения и понимания.

Прочитав кучу статей, мы выясняем, что рекомендуемым языком описания онтологий является OWL. Пока что версия 2 еще не принята(?), поэтому используем 1. Перевод описания OWL 1, перевод рабочей версии OWL2 для общего ознакомления.

Итак, теперь необходимо создать словарь, то есть описание структуры онтологии. Сами тройки мы буем хранить отдельно в базе данных, в репозитории. Конечно, при разработке словарей нужно максимально использовать уже существующие. Но мы хотим попробовать сами сделать что-то незамысловатое.

Допустим, у нас есть работающая обычная база каких-то условных терминов с описаниями. Пусть пользователи создают термины, задают отношения типа «содержит» и добавляют к ним описания. Соответственно нам нужно записывать тройки:
1) «Термин» — «имеетОписание» — Текст описания;
2) «Термин» — «содержит» — «Термин».
То есть попробуем сохранить оба вида троек: аттрибутивные и ссылки. Если мы хотим дополнительно указывать источники заносимой информации, например авторов, то так как ни в OWL, ни в ARC нет(?) прямой поддержки четвёрок (quad), то придётся городить что-то дополнительное самим. Самым простоым решением является добавление поля вроде «Author» в таблицу «xxx_triple». В этой таблице ARC хранит триплеты и при создании тройки дополнительно добавлять туда значения.

OWL оперируется объектами и их свойствами, поэтому нам нужно создать тестовый словарь, содержащий:
1) класс «Термин»;
2) свойство Термина «содержит»;
3) свойство Термина «имеетОписание»;

Онтологию можно описывать в разных нотациях. Оригинальной для OWL является OWL/XML, но можно и перевести в «старый», но везде используемый формат RDF/XML. Попробуем, для тестирования используя программу по редактированию онтологий, например Protege, создать словарь в RDF/XML. Что приятно, к Protege можно добавить плагин OwlViz, позволяющий увидеть граф на схеме.

<?xml version="1.0"?>
<rdf:RDF

// объявляем путь (префикс) к нашему словарю
xmlns:test ="http://www.yourdomen.ru/ontology#"
// объявляем пути к базовым словарям, на которых все построено (надо ли?)
xmlns:owl ="http://www.w3.org/2002/07/owl#"
xmlns:rdf ="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:xsd ="http://www.w3.org/2001/XMLSchema#">

// по желанию можно будет здесь указать свойства онтологии
<owl:Ontology rdf:about="">
</owl:Ontology>

// создаем классы
<owl:Class rdf:ID="#Termin"/>

// задаём свойства
<owl:ObjectProperty rdf:ID="includes">
// объект и субъект должны быть Терминами
<rdfs:domain rdf:resource="#Termin"/>
<rdfs:range rdf:resource="#Termin"/>
</owl:ObjectProperty>

<owl:ObjectProperty rdf:ID="#hasDescription">
// объект: термин, субъект: текст
<rdfs:domain rdf:resource="#Termin"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:ObjectProperty>

</rdf:RDF>


Ссылки по SPARQL в ARC:
semanticweb.narod.ru/3.html
jena.hpl.hp.com/~afs/SPARQL-Update.html
www.w3.org/TR/rdf-sparql-query
arc.semsol.org/docs/v2/sparql+

Имя или адрес субъекта, объекта и предиката задаётся IRI (вроде URL, только не имеет обязательства на существование). IRI записывается двумя способами:
1) с помощью префиксов. Префикс задается в начале запроса и указывается для нужных IRI. Само название префикса не имеет значения — в разных запросах они могут быть разными.
PREFIX vCard: <www.w3.org/2001/vcard-rdf/3.0#>
vCard:FN

2) относительные адреса в угловых скобках:
<somewhere/JohnSmith>
В начале запроса можно указать базовую приставку, которая будет добавляться в начале тех IRI, которые заканчиваются на «/».
BASE <somewhere>
<JohnSmith/>

Текстовый предикат (литерал) задаётся просто текстом в кавычках, одинарных или двойных — неважно. Для него также можно указывать дополнительно язык и тип данных. По-умолчанию тип: текстовый.
‘текст’
Переменные указываются с помощью знака $ или? Наверное, имеет смысл использовать ?, чтобы $aaa не распознавалось php как переменная внутри запроса в двойных кавычках.
SELECT ?title WHERE { <A> <B> ?title .}

Можно ставить тройку в тройку, используя «промежуточные узлы» с помощью квадратных скобок или приставок «_:». Пример: узнать деда Мишы:
som:haveDad [ som:haveDad $x ]
Комментарии к коду задаются решеткой. Тройки в запросе обязательно оканчиваются точками и группируются фигурными скобками. Если очень хочется, то можно одинаковый субъект записывать только для первой тройки, а последующие при этом разделять точками с запятой:
{ <example/book3> dc:title "A new book" ;
dc:creator "A.N.Other" .}


Сам SPARQL содержит только команду SELECT. В ARC используются дополнения к языку, позволяющие использовать в запросе слова: AVG / COUNT / MAX / MIN / SUM (SPARQL+), INSERT, DELETE и т. д. (SPARQL/Update).

Команда INSERT
В оригинале формат:
INSERT { template } [ WHERE { pattern } ]
Например:
PREFIX dc: <purl.org/dc/elements/1.1>
INSERT { <example/egbook3> dc:title "This is an example title" . }

Внутри хранилища могут быть различные области хранения троек для разных проектов, называемые графами (graph). Поэтому, если их несколько, то в запросе можно указать нужный граф с помощью INTO и URI графа:
INSERT INTO <example.com> { <#foo> "baz" . }
Есть слово DATA, зачем нужно — непонятно, вроде бы для указания того, что данные вставляются в хранилище. А просто INSERT тогда куда?
Итак, для вставки тройки в ARC нужна команда:
$rs = $store->query("INSERT INTO
{ <testSubject> <testPredicate> <testObject>.}");
Результат можно посмотреть через print_r($rs); в виде:
Array ( [query_type] => insert [result] => Array ( [t_count] => 1 [load_time] => 0.158 ) [query_time] => 0.283192873001 )

Если такой тройки ещё в базе нет, то запишется в виде записи из трех полей на id ресурсов, которые хранятся каждый в своей таблице.
Есть неясность: вместо указанного в запросе имени графа () в базу сохраняется приставка, соответствующая названию базы данных, то есть db/testSubject.
В запрос с INSERT нельзя вставлять переменные, для этого нужно использовать дополнительное слово CONSTRUCT:
INSERT INTO <example.com/inferred>
CONSTRUCT { ?s foaf:knows ?o .} WHERE { ?s xfn:contact ?o .}


Команда SELECT
$query = "SELECT ?poo WHERE { ?poo <testPredicate> <testObject> . }";
if ($row = $store->query($query, 'row')) echo $row['poo'];

‘row’ — это параметр вывода результатов. Их список можно посмотреть на arc.semsol.org/docs/v2/store . Результатом запроса будет элемент массива с именем из запроса (poo). Пока неясно, как указывать нужный граф при SELECT, а так вроде работает. Также пока даже примерно не представляю, как использовать в запросах созданный словарь в виде отдельного файла. Может подскажет кто.

------------------------------------------------------
Это не финальная версия статьи, возможны неточности.
Просьба спокойно указывать на них, а также предлагать решения. По вашим комментариям пост будет изменяться, пока не получится что-то работающее. Надеюсь на вашу помощь.
P.S. Это встроенный редактор поста такой кривой, или я что-то не то делаю: приходится все угловые скобки на & g t ; вручную менять, <code> как-то не закрывается...
Tags:
Hubs:
+9
Comments 20
Comments Comments 20

Articles