65.9K
CodeProject 正在变化。 阅读更多。
Home

开发 Semantika 客户端应用程序

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.67/5 (2投票s)

2014 年 6 月 25 日

CPOL

7分钟阅读

viewsIcon

12742

downloadIcon

284

这是关于 Semantika 的第三篇也是最后一篇文章。本文将指导您开发一个简单的“hello-world”查询应用程序。

引言

这是我最近开发的一个名为 Semantika 的开源项目系列文章的第三篇。如果您还没有阅读第一篇或第二篇文章,请跳转到下面的链接,阅读完后再回到这里。

第一部分 - Semantika DRM 简介

第二部分 - 关系到领域映射建模

在这最后一篇文章中,我将向您展示如何通过创建一个简单的查询应答应用程序来使用该平台。我们将制作的应用程序是一个控制台应用程序。它使用文本终端来读写输入/输出。这将是一个 Java 编程教程。

该应用程序已在 OSX 10.8 和 Java 7 SDK 上进行了测试并成功运行。

背景

我假设我的读者已经具备 Java 编程、关系数据库和 XML 的一些背景知识。拥有 OWL、RDF 和 SPARQL 等语义 Web 技术方面的先验知识将会有益。

一个“Hello World”教程

在本节中,我将一步一步地指导您创建应用程序。您可以在本文的页面上找到应用程序源代码。但是,我建议您跟随每个步骤并亲自进行操作。这是一个简短的教程,不会花费您太多时间。

先决条件

您需要下载 Semantika 发行包(本文撰写时最新版本为 1.6)。您可以通过单击大绿色按钮从其网站 http://obidea.com/semantika 下载。

或者,在命令行中使用 wget

wget https://github.com/obidea/semantika-api/releases/download/v1.6/semantika-dist-1.6.zip

解压该软件包,我们将该目录路径定位为 SEMANTIKA_HOME 变量。

运行数据库

我们需要的第一个组件是正在运行的数据库。为此,我准备了一个模拟数据库,您可以 在此处下载

或者,再次使用 wget

wget https://github.com/obidea/semantika-api/releases/download/v1.1/h2-semantika_24-02-2014.zip

zip 文件中包含一个独立的 H2 数据库。它包含存储在 6 个表中的有关员工的数据。数据模式如下所示

要运行数据库,请双击文件 h2.sh(在 Unix 中)或 h2.bat(在 Windows 中)。

或者,使用命令行

./h2.sh

它将立即在您的默认浏览器中打开 H2 Web 控制台。

重要提示: Semantika 通过 JDBC 平台进行数据连接。它需要一个与底层数据库关联的 JDBC 驱动程序。您可以在 H2 文件夹中找到名为 h2-1.3.168.jar 的驱动程序,并将其文件复制(不是剪切)到 SEMANTIKA_HOME/jdbc 文件夹。

模型资源

对于模型资源,我们将同时使用本体和映射模型。

我自己创建了用于描述公司员工的本体。这是一个简单的建模,我使用通常出现在职位层级中的标签以及员工、部门和经理之间的一些典型关系。本体模型的概述如下所示

这里 这里 下载本体和映射模型。

或者,使用 wget

wget https://raw.githubusercontent.com/obidea/semantika-api/master/model/empdb.owl
wget https://raw.githubusercontent.com/obidea/semantika-api/master/model/empdb.mod.xml

将这两个文件放入 SEMANTIKA_HOME/model 目录。您可能会注意到该文件夹中已经有几个文件。它们只是入门模板,您可以忽略或删除它们。

客户端 Java 代码

现在我们准备开始编写代码了。我们将创建一个控制台应用程序,该应用程序可以交互地接受用户的输入查询并显示结果。

这是完整的源代码

import java.io.*;

import com.obidea.semantika.app.Environment;
import com.obidea.semantika.app.ApplicationFactory;
import com.obidea.semantika.app.ApplicationManager;
import com.obidea.semantika.queryanswer.IQueryEngine;
import com.obidea.semantika.queryanswer.result.IQueryResult;

public class EmployeeApp
{
   private static IQueryEngine mQueryEngine;
   
   public static void main(String[] args) throws Exception
   {
      initialize();
      start();
      try {
         String queryString = readUserInput();
         executeQuery(queryString);
      }
      finally {
         stop();
      }
   }
   
   private static void initialize() throws Exception
   {
      ApplicationManager appManager = new ApplicationFactory()
         .setName("empdb-app")
         .addProperty(Environment.CONNECTION_URL, "jdbc:h2:tcp:///empdb")
         .addProperty(Environment.CONNECTION_DRIVER, "org.h2.Driver")
         .addProperty(Environment.CONNECTION_USERNAME, "sa")
         .addProperty(Environment.CONNECTION_PASSWORD, "")
         .setOntologySource("model/empdb.owl")
         .addMappingSource("model/empdb.mod.xml")
         .createApplicationManager();
      
      // Create the query engine
      mQueryEngine = appManager.createQueryEngine();
   }
   
   private static void start() throws Exception
   {
      mQueryEngine.start(); // get the connection
   }
   
   private static void stop() throws Exception
   {
      mQueryEngine.stop(); // release the connection resource
   }
   
   private static void executeQuery(String queryString) throws Exception
   {
      IQueryResult result = mQueryEngine.evaluate(queryString);
      printResult(result);
   }

   private static String readUserInput() throws Exception
   {
      System.out.println(); // space a bit
      System.out.println("Please type your query (use a period '.' sign to finish typing):");
      
      StringBuilder userQuery = new StringBuilder();
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      while (true) { 
         String str = br.readLine();
         if (str.equals(".")) {
            break;
         }
         userQuery.append(str).append("\n");
      }
      return userQuery.toString().trim();
   }
   
   private static void printResult(IQueryResult result)
   {
      int counter = 0;
      while (result.next()) {
         counter++;
         System.out.println(result.getValueList().toString());
      }
      System.out.println(counter + " row(s) returned");
   }
}

关于 API 的使用,有几点需要强调。正如您在 main 方法中看到的,有一些步骤使应用程序运行

  • initialize():此方法中的开头几行定义了如何使用一些配置参数创建 ApplicationManager。这些参数存储本地文件中的数据库连接字符串和资源位置。当它们有效时,您可以获得 QueryEngine 实例来处理查询请求。

  • start()stop():这两个方法控制数据库资源。这种分离允许开发人员自由地将连接设置放在应用程序代码中。但是,建议在打开引擎时始终停止它,以避免内存泄漏。

  • executeQuery():此方法将执行查询并返回一个 QueryResult 对象。API 提供了几种方法来访问结果以进行后处理和数据操作。

将上面的源代码复制粘贴并创建一个新文件 SEMANTIKA_HOME/EmployeeApp.java。在下一节中,我们将编译代码并运行一些示例查询。

执行查询

首先,我们需要编译源代码。在您的 SEMANTIKA_HOME 中,输入

javac -cp .:jdbc/*:lib/*:semantika-core-1.6.jar EmployeeApp.java

如果您在 Windows 系统中,请将 -cp 选项中的冒号“:”替换为分号“;”。

要运行该应用程序

java -cp .:jdbc/*:lib/*:semantika-core-1.6.jar EmployeeApp

这将显示一些类似于下面快照的日志消息,然后暂停并等待输入 SPARQL 查询。

2014-06-25 14:05:56,644 INFO  [application] Initializing ApplicationManager.
2014-06-25 14:05:56,955 INFO  [application] Loading [DATABASE] object...
2014-06-25 14:05:56,969 WARN  [connection] An empty password is being used.
2014-06-25 14:05:57,178 INFO  [application] Loading [ONTOLOGY] object...
2014-06-25 14:05:57,470 INFO  [application] Loading [MAPPING SET] object...
2014-06-25 14:05:57,980 INFO  [application] Creating knowledge base...
2014-06-25 14:05:57,981 INFO  [application] Optimizing knowledge base...

Please type your query (use a period '.' sign to finish typing):

SPARQL 查询

解释 SPARQL 的全部功能可以写成 一本书。在本小节中,我将简要介绍其概念和语法,以便您能有所了解。

SPARQL(发音:sparkle)是一种用于 RDF 数据库的查询语言,它通过模式匹配工作。这意味着什么?首先,您需要了解什么是 RDF 数据。从概念上讲,RDF 数据是图数据,由一个 3 部分的字符串组成:主语-谓语-宾语。一些例子

  • ​<http://example.org/Subject> <http://example.org/Predicate> <http://example.org/Object>
  • <http://obidea.com/ex/ontology/empdb#10001> <http://obidea.com/ex/ontology/empdb#lastName> "Facello"
  • emp:10001 rdf:type emp:Employee

SPARQL 是一种检索此类数据的查询语言。其语法遵循相同的三元组模式,例如

SELECT ?employee ?lname
WHERE {
   ?employee <http://obidea.com/ex/ontology/empdb#lastName> ?lname .
}

如果我们对上述示例数据应用此查询,结果将是 <http://obidea.com/ex/ontology/empdb#10001> 和 "Facello"。正如您现在所知,结果是通过将查询中的三元组模式与数据集中模式进行匹配来确定的。

关于 SPARQL 语法的实际注意事项:使用快捷方式避免重复!

  • 使用分号表示后续三元组使用相同的**主语**。下面的两行三元组是等效的。

    ?x :firstName ?fname; :lastName ?lname .
    ?x :firstName ?fname . ?x :lastName ?lname .
  • 使用逗号表示后续三元组使用相同的**谓语**。下面的两行三元组是等效的。

    ?x :memberOf :dept1, :dept2 .
    ?x :memberOf :dept1 . ?x :memberOf :dept2 .

SPARQL 的完整规范可在 http://www.w3.org/TR/sparql11-query/ 找到。您会发现这种查询语言具有类似于 SQL 的功能,例如 DISTINCT、FILTER、UNION、ORDER BY、OFFSET、LIMIT、函数等。

快速示例

在本小节中,我将为您提供几个 SPARQL 示例,您可以复制粘贴并尝试使用该应用程序执行。

查询 1:显示所有员工的全名

PREFIX :   <http://obidea.com/ex/ontology/empdb#>
SELECT ?fname ?lname
WHERE
{ ?x :firstName ?fname;
     :lastName ?lname .
}

(预期输出:50,000 条记录)

查询 2:显示所有工程师

PREFIX :   <http://obidea.com/ex/ontology/empdb#>
SELECT ?fname ?lname
WHERE
{ ?x a :Engineer;
     :firstName ?fname;
     :lastName ?lname .
}

(预期输出:5,185 条记录)

查询 3:显示人力资源经理

PREFIX :   <http://obidea.com/ex/ontology/empdb#>
SELECT ?fname ?lname
WHERE
{ ?manager :leads ?department;
           :firstName ?fname;
           :lastName ?lname .
  ?department :deptName "Human Resources" .
}

(预期输出:1 条记录 [=Xiong Peron])

查询 4:显示所有在 1995 年 1 月 1 日之后被雇佣且工资超过 50K 的员工

PREFIX :   <http://obidea.com/ex/ontology/empdb#>
SELECT ?fname ?lname ?hiredate ?salary
WHERE
{ ?x :firstName ?fname;
     :lastName ?lname;
     :hireDate ?hiredate;
     :salaryAmount ?salary .
  FILTER ( ?hiredate > '1995-01-01' && ?salary > 50000 )
}

(预期输出:3,367 条记录)

查询 5:显示所有工资高于其老板的销售部门员工

PREFIX :   <http://obidea.com/ex/ontology/empdb#>
SELECT ?fname ?lname
WHERE
{ ?boss :leads ?dept;
        :salaryAmount ?bossSalary .
  ?emp :memberOf ?dept;
       :firstName ?fname;
       :lastName ?lname;
       :salaryAmount ?empSalary .
  ?dept :deptName "Sales" .
  FILTER ( ?empSalary > ?bossSalary && ?emp != ?boss )
}

(预期输出:362 条记录)

示例执行

别忘了最后一行末尾的**句点**!

$ java -cp .:jdbc/*:lib/*:semantika-core-1.6.jar EmployeeApp
2014-06-25 14:05:56,644 INFO  [application] Initializing ApplicationManager.
2014-06-25 14:05:56,955 INFO  [application] Loading [DATABASE] object...
2014-06-25 14:05:56,969 WARN  [connection] An empty password is being used.
2014-06-25 14:05:57,178 INFO  [application] Loading [ONTOLOGY] object...
2014-06-25 14:05:57,470 INFO  [application] Loading [MAPPING SET] object...
2014-06-25 14:05:57,980 INFO  [application] Creating knowledge base...
2014-06-25 14:05:57,981 INFO  [application] Optimizing knowledge base...

Please type your query (use a period '.' sign to finish typing):
PREFIX :   <http://obidea.com/ex/ontology/empdb#>
SELECT ?fname ?lname
WHERE
{ ?x :firstName ?fname;
     :lastName ?lname .
}
.
[fname=Georgi; lname=Facello]
[fname=Bezalel; lname=Simmel]
[fname=Parto; lname=Bamford]
[fname=Chirstian; lname=Koblick]
[fname=Kyoichi; lname=Maliniak]
[fname=Anneke; lname=Preusig]
[fname=Tzvetan; lname=Zielinski]
.
.
.
[fname=Hein; lname=Zizka]
[fname=Siddarth; lname=Krogh]
[fname=Jaana; lname=Bolotov]
[fname=Ottavia; lname=Keirsey]
[fname=Piyush; lname=Spataro]
[fname=Heekeun; lname=Emmart]
50000 row(s) returned

最终结论

在这最后一篇文章中,我想表达我在开发 Semantika 背后的动机,遵循 Simon Sinek 的黄金圈法则:为什么-如何-是什么

为什么:我想挑战关系数据库技术中似乎无法满足当前数据管理趋势的问题。

如何:通过应用开放世界数据库的概念,为关系技术带来新的视角。这个概念和新视角是当前语义 Web 技术的外在表现。

是什么:碰巧我开发了 Semantika 来努力实现这一目标。该解决方案侵入性较低,并具有令人鼓舞的前景。您想试试吗?

我希望最后一篇文章能够说服您开始了解并使用 Semantika。关于这个平台还有很多话题可以讲述。未讲述的功能。基准测试。未来计划。所以也许我以后会写更多的故事。

结语

在其当前版本 1.6 中,Semantika 仍处于早期阶段。仍有许多工作需要实现,算法也需要更多的优化工作。所以是的,仍然有很大的改进空间。尽管我不得不说该产品本身仍然缺少实际用例。

在我的 Trello 账户中,我有一长串令人兴奋的新功能,但有时我很难决定哪个应该先完成。此外,我不确定这些功能是否真的令人兴奋,因为我还没有听到您的声音。我希望通过写这些文章,我已经做了一些有用的、友好的事情:挥手引起您的注意。

对于那些对这个想法感兴趣并希望做出贡献的人,请访问我们的 GitHub 存储库:https://github.com/obidea/semantika-core。源代码就在那里,您可以随意下载、研究和修改它。

对于开发人员和最终用户,您可以获取二进制代码并开始使用它:http://obidea.com/semantika

历史

25-06-2014:

  • 原始文章。

26-06-2014:

  • 在示例应用程序源代码中添加缺失的下载链接
  • 在示例执行部分显示结果
© . All rights reserved.