Семантическая Сеть

индекс
94,74

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

Проблема: сложность практического освоения технологий семантической сети начинающими.

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

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

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


Исходные условия: распространенная, доступная, понятная и документированная связка 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> как-то не закрывается...
+9
21 января 2009, 17:13
11

комментарии (20)

0
ur001 #
Спасибо большое за статью. Надеюсь вы на этом не остановитесь.
У меня никак не доходят руки разобраться в этих штуках, но очень интересно.

Если ваша карма подрастёт (чего вам желаю) советую переместить топик в блог семантическая сеть или web 3.0

Кстати, недавно появился перевод OWL2 на сайте SHCERBAK.NET
0
namata #
Добавил ссылку.
0
ur001 #
Да, ещё объясните пожалуйста «на пальцах» связь OWL и RDF если можно. Я понимаю что RDF — это тройка [объект, субъект, предикат], а OWL — язык описания онтологий. OWL может использовать словарь RDF — верно? Короче я немного тут запутался
0
namata #
RDF — это общее название всей модели, технологии представления данных в виде троек, или фактов, или более оффициально триплетов. Видов непосредcтвенно записи троек существует несколько: оригинальный RDF/XML, различные краткие нотации вроде N3 или просто изображение в виде графа. Так как помимо самих троек необходимо делать соглашения о смысле и взаимосвязях предикатов (типов ссылок или ребер графа от объекта к субъекту в тройке), то их описывают с помощью RDF Schema (RDFS).
Язык или скорее набор правил OWL предлагает запись всей онтологии с помощью классов, их отношений и конкретных объектов (индивидов). Опять же OWL можно выразить разными способами: тем же RDF/XML, OWL/XML, функциональным или «манчестерским» синтаксисом и т.д. Можно сказать, что OWL сомещает в себе запись троек RDF с их описанием RDFS на более высоком продуманном уровне, но и более сложном для освоения и понимания.
Если не прав, дополните пожалуйста.
0
ur001 #
Ага. Спасибо. Я примерно так себе это и представлял, только не было ясности
0
ur001 #
Чего-то никто тут не пишет :(

Попробую сам. Как я понимаю у «Термина есть Описание», и мы хотим выразится таким образом чтобы описание принадлежало определённому автору. Некий автор добавил к Термину такое-то описание.

Получается что класс Термин имеет свойство Описание. Описание — это класс имеющий два свойства. Первое свойство ОписанияТекстОписания. Второе ПринадлежитАвтору. Автор в свою очередь — это класс являющийся подклассом класса Персона
// создаем классы
<owl:Class rdf:ID="Termin"/>
<owl:Class rdf:ID="Author"/>
<owl:Class rdf:ID="Description"/>

// задаём свойства
<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="#Description"/>
</owl:ObjectProperty>

< owl:ObjectProperty rdf:ID="hasAutor">
	// объект: описание, субъект: автор
	<rdfs:domain rdf:resource="#Description"/>
	<rdfs:range rdf:resource="&xsd;string"/>
</owl:ObjectProperty>

<owl: owl:DatatypeProperty y rdf:ID="hasText">
	// объект: описание, субъект: текст
	<rdfs:domain rdf:resource="#Description"/>
	<rdfs:range rdf:resource="&xsd;string"/>
</owl:ObjectProperty>


P.S. Вы ничего не напутали в "#"? И, правильно ли я описал свойство hasText как DatatypeProperty?
0
ur001 #
ой, там в конце опечатка
<owl:DatatypeProperty rdf:ID="hasText">
0
namata #
Здесь пока может быть много чего напутано. Пытаемся разобраться.

Основная идея, которую мне хочется реализовать, это указание для каждой(!) тройки её автора. Создавать отдельное свойство hasAuthor для каждого вида предиката кажется не самым лучшим, хотя и работающим вариантом. Желательно найти более универсальный способ. Например навреное можно как-то создать иерархию всех свойств (предикатов) и вынести свойство «hasAuthor» на самый верх. Также можно конечно реализовывать это вне RDF вообще. Например просто хранить дополнительные данные об авторе каждой тройки в БД.

Различают два типа свойств:
1) свойства — значения, отношения между представителями классов и RDF-литералами или типами данных, определяемых XML Schema;
2) свойства-объекты, отношения между представителями двух классов.
Создаётся впечатление, что 1 задаётся с помощью ObjectProperty, а 2 — с помощью DatatypeProperty. Тогда если author у нас это просто внешний id, то hasAutor тоже будет DatatypeProperty.

С #, путями к файлам это отдельный вопрос. Хотелось бы получить подсказку по поводу как указывать пути к файлу словаря, чтобы обработчик понимал запросы с учетом его.
0
ulfurinn #
Можно воспользоваться тем, что каждая тройка RDF сама по себе может быть представлена как объект rdf:Statement со свойствами rdf:subject, rdf:object и rdf:predicate; но тогда, при наивном хранении, в три раза возрастает объем представления одной и той же информации, т.к. каждую тройку надо держать в расписанном виде. Если движок дает возможность хранить мета-факты в более компактном виде (ссылкой на ID тройки в таблице или еще как-нибудь), то вполне даже вариант.
0
namata #
ARC хранит тройки в 4-х таблицах. У кажой тройки есть свой id, поэтому хранение автора тройки можно реализовать сторонними средствами.
+2
shcherbak #
насчет автора для каждой тройки —

Я так понимаю речь идет представлении служебной информации в RDF, например, об авторе или создателе конкретной тройки RDF.

Потому что «вишня» как экземляр класса Ягода, у которой автор Вася Пупкин, как то глупо звучит. Значит, вероятно всего вы имеете ввиду служебную информации тогда вынос hasAuthor в корень иерархии в общем ничего не дает. Хотя например в редакторе онтологий Protege есть наборы служебных классов, выполняющие подобные функции, которые не отображаются в основном дереве представления RDF

C другой стороны, я не зря вспомнил, создателя. Просто если с филосовской точки зрения посмотреть — то все имеет своего создателя, например, даже мир имеет создателя.

В любом случае, конструкцию автор триплета лучше вынести во вне, до лучших времен — пока не введут синтаксическую конструкцию в OWL об авторстве.

кстати, под конец моего рассуждения, пришла мысль о том, что есть же Dublin Core, как расширение RDF

в котором рассматривается понятие авторства информации… А это значит, что информация из другого пространства имен, не будет связана по смыслу с «основными» знаниями о предметной области.

Так что все просто, ответ на ваш вопрос — введение Dublin Core в ваш проект ))))))))))))))
+2
shcherbak #
Автору статьи +1 к карме за то, что я не смог с ходу найти ответ на возникший вопрос о служебной информации в RDF
+1
ur001 #
О, здорово. Автор shcherbak.net подтянулся :)
Даже целую статью написал.

А как насчёт ARC? Может, авторство триплета и можно описать с помощью Дублинского Ядра, но сможем мы потом использовать готовое хранилище и делать SPARQL запросы с учётом авторства? Например выбрать все утверждения автора об объектах определённого класса?
0
namata #
Пока что я не могу понять, как составлять запросы. Как в запросу указать что нужно использовать файл словаря, в котором прописаны отношения классов, в том числе словарь DC?
0
shcherbak #
Проще всего вам посмотреть, как реализована поддержка Dublin Сore в Protege 4.0 и сделать по аналогии — а именно откройте файл OWL и посмотрите какие пространства имен вам надо добавить и какие свойства.
А насчет запросов — я не помню — можно ли в ARC использовать отличные пространства имен по умолчанию в запросах. хотя для sparql это не должно быть преградой. Скоро будет готов перевод по SPARQL, тогда и отвечу
0
shcherbak #
насчет ARC не знаю, но расширение RDF через DC это естественная операция, проблем с SPARQL не должно быть
0
sally #
Очень хорошая идея насчет Dublin Core
В protege 4.0 это, кстати, уже есть
Когда добавляешь Annotation, допустим, к классу, там есть Dublin Core Annotation URI's — «creator» или «contributor»
+1
ur001 #
Продолжение в студию! :)
0
namata #
Кое-что добавил, что узнал.
0
ur001 #
Спасибо )))

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.