Статьи

И теперь для чего-то совершенно другого: использование OWL с Neo4j

Первоначально автор Stefanie Wiegand

Почему вы хотите это сделать?

OWL существует уже давно и используется для различных семантических приложений. Онтологии находятся в свободном доступе и помогают разработчикам создавать модели для реальных сценариев. Они могут быть созданы, объединены и обогащены с использованием правил SWRL и таких рассуждений, как
Отшельник или
Пеллет . Причины создания такого представления данных различны: обработка на естественном языке, повторное использование данных в разных доменах или контекстуализация — это лишь некоторые из многих. Полученные данные затем сохраняются в базе знаний, из которой они могут быть получены с помощью запросов SPARQL. Но если все это уже есть, какой смысл объединять его с базой данных графов?

Хотя SPARQL, безусловно, имеет свои сильные стороны, такие как одновременное использование разных онтологий, и сходство с хорошо известным SQL, он также имеет недостатки. Тройные хранилища, которые являются отправной точкой для большинства приложений SPARQL, занимают
много дискового пространства по сравнению с реляционными базами данных. Они также медленны для очень больших наборов данных.

Neo4j хранит целые графики в отличие от «просто» троек. Он имеет простой в освоении и использовании язык запросов и веб-интерфейс с графическим интерфейсом, который позволяет пользователям легко просматривать и изучать график. Кроме того, это быстро для запросов и
очень хорошо масштабируется для обработки больших наборов данных.

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

  • Количество и частота и связность входящих данных
  • Важность скорости и размера
  • Тип запроса, выполняемого в базе данных

Игровая площадка

Существует
онтология
PROV-O , которая моделирует причинно-следственную связь между деятельностью, агентами и организациями. Эта концепция довольно абстрактна, но полезна для ответа на ряд вопросов, связанных с происхождением сущностей (и что могло повлиять на них на протяжении всей их жизни). Это применимо к ряду областей, например, к социальным сетям («Кто был автором поста в блоге, который повлиял на Питера, чтобы написать его мэшап?») Или к экспериментам («Кто был последним, кто получил доступ к эксперименту до того, как он провалился, и когда он получил к нему доступ? »).

PROV-O используется в
BonFIREпроект, который является многопользовательской облачной экспериментальной и тестовой средой. Есть люди (агенты), проводящие эксперименты с использованием ресурсов (сущностей). На уровне инфраструктуры для выполнения своих экспериментов они создают, используют и уничтожают вычислительные узлы, хранилища и виртуальные сети (объекты). После завершения эксперимента они загружают результаты (объекты) из виртуальной машины для дальнейшего анализа. На эти результаты влияет большое количество действий и агентов, и часто бывает трудно определить, каким был такой результат, кто участвовал в его формировании или почему он отличается от других результатов. Используя происхождение, на эти вопросы можно ответить.

Препараты

В BonFIRE данные поступают в
RabbitMQ в виде набора сообщений JSON, которые выглядят следующим образом:

{"timestamp":1375801302,"eventType":"state.shutdown","objectType":"compute","objectId":"/locations/server1/computes/123","groupId":"group1","userId":"bert"}

В этом случае Bert выключил вычислительный узел 123, расположенный на сервере server1. Это сообщение заполняется в классы Java, которые используются для их преобразования («вручную») в тройки. Используя одно сообщение из приведенного выше примера, мы получаем несколько тройок, которые будут выглядеть примерно так:

:Action_state.shutdown_1375801302 rdf:type :Action
:Compute_/locations/server1/computes/123 rdf:type :Compute
:Compute_/locations/server1/computes/123 prov:invalidatedBy :Action_state.shutdown_1375801302
:Experimenter_Bert rdf:type :Experimenter
:Experimenter_Bert prov:wasAssociatedWith :Action_state.shutdown_1375801302
...

Используемые префиксы определены в онтологии, в которую эти тройки будут импортированы.

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

Эти тройки могут быть добавлены в онтологию с помощью
OWLRDFConsumer.класс из OWLAPI. Это добавляет тройки к онтологии, где может быть вызван аргумент для обогащения данных. Пока это не совсем особенное. Интересный момент следует после того, как рассуждение состоялось.

Получение Graphy

Теперь в памяти находится этот объект онтологии, который содержит саму онтологию, а также индивидуумов, пришедших из троек. Теперь его можно просто сохранить в базе знаний, но если бы это было так, вы бы не читали об этом здесь 🙂

Онтология — это граф. У него есть верхний узел (owl: Thing) и классы, его расширяющие. Есть люди, которые принадлежат классам и свойствам объекта, связывающим людей. Отдельные лица могут иметь свойства данных и аннотации, которые могут быть представлены как свойства узла и свойства отношения или как типы отношения.

Импортировать онтологию довольно просто:

Шаг 1

Единственный объект, который вам нужен, это объект онтологии, созданный ранее. Он также может быть загружен из файла, который не имеет значения.

private void importOntology(OWLOntology ontology) throws Exception {
    OWLReasoner reasoner = new Reasoner(ontology);
       
        if (!reasoner.isConsistent()) {
            logger.error("Ontology is inconsistent");
            //throw your exception of choice here
            throw new Exception("Ontology is inconsistent");
        }
        Transaction tx = db.beginTx();
        try {

Шаг 2

Создайте начальный узел в Neo4j, представляющий узел owl: Thing. Это корневой узел графа, который мы собираемся создать.

Node thingNode = getOrCreateNodeWithUniqueFactory("owl:Thing");

Шаг 3

Получить все классы, определенные в онтологии, и добавить их в граф.

             for (OWLClass c :ontology.getClassesInSignature(true)) {
                String classString = c.toString();
                if (classString.contains("#")) {
                    classString = classString.substring(
                     classString.indexOf("#")+1,classString.lastIndexOf(">"));
                }
                Node classNode = getOrCreateNodeWithUniqueFactory(classString);

Шаг 4

Узнайте, есть ли у них супер классы. Если они это сделают, свяжите их. Если они этого не делают, ссылка на сову: вещь. Обязательно только ссылки на прямые супер классы! Тип отношения, используемый для выражения свойства rdf: type, является пользовательским с именем «isA».

                 NodeSet<OWLClass> superclasses = reasoner.getSuperClasses(c, true);

                if (superclasses.isEmpty()) {
                    classNode.createRelationshipTo(thingNode,
                     DynamicRelationshipType.withName("isA"));   
                } else {
                    for (org.semanticweb.owlapi.reasoner.Node<OWLClass>
                     parentOWLNode: superclasses) {
                       
                        OWLClassExpression parent =
                         parentOWLNode.getRepresentativeElement();
                        String parentString = parent.toString();
                       
                        if (parentString.contains("#")) {
                            parentString = parentString.substring(
                             parentString.indexOf("#")+1,
                             parentString.lastIndexOf(">"));
                        }
                        Node parentNode =
                         getOrCreateNodeWithUniqueFactory(parentString);
                        classNode.createRelationshipTo(parentNode,
                         DynamicRelationshipType.withName("isA"));
                    }
                }

Шаг 5

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

                 for (org.semanticweb.owlapi.reasoner.Node<OWLNamedIndividual> in
                 : reasoner.getInstances(c, true)) {
                    OWLNamedIndividual i = in.getRepresentativeElement();
                    String indString = i.toString();
                    if (indString.contains("#")) {
                        indString = indString.substring(
                         indString.indexOf("#")+1,indString.lastIndexOf(">"));
                    }
                    Node individualNode = 
                     getOrCreateNodeWithUniqueFactory(indString);
                                             
                    individualNode.createRelationshipTo(classNode,
                    DynamicRelationshipType.withName("isA"));

Шаг 6

Для каждого человека получите все свойства объекта и все свойства данных. Добавьте их на график в качестве свойств узла или отношений. Обязательно получите все аксиомы, а не только утвержденные.

                     for (OWLObjectPropertyExpression objectProperty:
                     ontology.getObjectPropertiesInSignature()) {

                       for  
                       (org.semanticweb.owlapi.reasoner.Node<OWLNamedIndividual> 
                        object: reasoner.getObjectPropertyValues(i,
                        objectProperty)) {
                            String reltype = objectProperty.toString();
                            reltype = reltype.substring(reltype.indexOf("#")+1,
                             reltype.lastIndexOf(">"));
                           
                            String s =
                             object.getRepresentativeElement().toString();
                            s = s.substring(s.indexOf("#")+1,
                             s.lastIndexOf(">"));
                            Node objectNode =
                             getOrCreateNodeWithUniqueFactory(s);
                            individualNode.createRelationshipTo(objectNode,
                             DynamicRelationshipType.withName(reltype));
                        }
                    }

                    for (OWLDataPropertyExpression dataProperty:
                     ontology.getDataPropertiesInSignature()) {

                        for (OWLLiteral object: reasoner.getDataPropertyValues(
                         i, dataProperty.asOWLDataProperty())) {
                            String reltype =
                             dataProperty.asOWLDataProperty().toString();
                            reltype = reltype.substring(reltype.indexOf("#")+1, 
                             reltype.lastIndexOf(">"));
                           
                            String s = object.toString();
                            individualNode.setProperty(reltype, s);
                        }
                    }
                }
            }
            tx.success();
        } finally {
            tx.finish();
        }
    }

Вот и все, вы сделали! Теперь самое интересное: запрос онтологии!

Graphwalking

Это график, который сейчас находится в базе данных:

Он обладает онтологией, а также всеми индивидуумами и свойствами, представленными в их «естественной» форме. Теперь запрос может начаться. Является ли это простым запросом, чтобы выяснить, что произошло с конкретной виртуальной машиной (сущностью) во время ее жизненного цикла

START e=node:name(name="experiment123"), ag=node:name(name="Agent")
MATCH e-[r:hadActivity]->ac-->a-[:isA*]->ag
RETURN distinct e.name as experiment, type(r) as relationship, a.name as agent
ac.name as activity, ac.startedAtTime as starttime, ac.endedAtTime as endtime
ORDER BY starttime

Вывод

Protege поставляется с простой визуализацией и возможностью выполнять запросы SPARQL. Neo4j имеет шифр, который делает запросы к импортированной онтологии намного более интуитивно понятными — онтологии в конце концов являются графами. Также интерфейс webadmin позволяет лучше «исследовать» граф. Время не является проблемой в этом случае, потому что импорт онтологий не является критичным по времени. Это делается только один раз после окончания эксперимента и импортирует всю онтологию. Для онтологии, содержащей несколько часов экспериментальных данных, импорт занимает всего несколько секунд. Как только график импортирован, запросы выполняются быстро, что делает его отличным инструментом для анализа и визуализации онтологий.