Semantic Web for PHP

创建、读、更新和删除(Create/Read/Update/Delete,CRUD)操作是最基本的数据库操作,但是它们也是最重要的操作。CRUD操作通常是使用关系数据库系统中的结构化查询语言(Structured Query Language,SQL)完成的。随着Web 变得更加具有面向数据特性,因此需要从基于SQL的CRUD操作转移到基于语义 Web的CRUD操作。了解如何使用PHP 通过基于语义的Web 执行 CRUD操作。

常用缩写词

API — 应用程序编程接口(Application Programming Interface)

CRUD — 创建/读/更新/删除(Create/Read/Update/Delete)

HTTP — 超文本传输协议(Hypertext Transfer Protocol)

MVC — 模式—视图—控制器(Model-View-Controller)

OOP — 面向对象的编程(Object-Oriented Programming)

RDF — 资源描述框架(Resource Description Framework)

SPARQL — 简单协议和RDF 查询语言(Simple Protocol and RDF Query Language)

SQL — 结构化查询语言(Structured Query Language)

UI —用户界面(User interface)

W3C — 万维网联盟(World Wide Web Consortium)

在开发 Web 应用程序时,为逻辑层和UI 层创建放置服务器端代码的数据库结构是一种标准实践。要连接到数据库,服务器端代码需要执行一些基本的创建、更新、删除和— 最重要的— 读取记录等操作。由于Web 应用程序的后台数据库通常都是关系数据库,因此这些 CRUD操作都是使用众所周知的SQL语言执行的。但是,随着Web开发越来越多地采用面向对象的编程(OOP),模型也随之发生改变。

资源描述框架(Resource Description Framework,RDF)是描述对象同时保留数据含义的理想方法。简单协议和RDF 查询语言(Simple Protocol and RDF Query Language,SPARQL — 发音为 “sparkle”)是通常用于针对该数据进行查询的语言,因为它在语句构成上匹配 RDF 本身的结构。RDF和SPARQL 都是所谓语义 Web 栈(semantic Web stack)中的技术。

要彻底地应用语义 Web 理念,您可以使用SPARQL将传统的Web开发技术应用到 RDF 数据中。本文将展示如何使用简化的模式—视图—控制器(Model-View-Controller,MVC)设计模型、PHP 服务器端脚本语言和SPARQL 连接到 RDF — 与使用关系数据库系统中的SQL 相反。

SQL和SPARQL CRUD操作

先决条件

本文假定您基本了解 SQL、PHP和Web 应用程序开发。了解语义 Web 也十分有利。要对基于语义 Web的数据运行 create、update和delete 命令,需要具有支持 SPARQL/Update 规范的语义 Web 数据库。

在使用SQL和SPARQL 进行开发时,需要查看一下CRUD操作之间的异同。

清单1 显示了read操作的SQL代码。

SELECT realname, dob, location
FROM UserTable
WHERE realname = "John Smith";

将这段基于SQL的代码与清单2中所示的基于SPARQL的代码相比较。采用这两个read操作的原因在于它们最易于理解、实现和说明。这对于SQL和SPARQL 来说都是一样的。

清单 2. read 操作的 SPARQL 代码

PREFIX foaf:
<http://xmlns.com/foaf/0.1/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT ?uri ?name ?dob ?location
FROM
<http://www.example.org/graph>
WHERE
{ ?uri
rdf:type foaf:Person ;
foaf:name "John Smith" ;
foaf:birthday
?dob ;
foaf:location ?location .
} ;

在比较两张清单时,您的第一个想法很可能是SPARQL 版本明显比 SQL 版本长很多。这是事实,但是请不要误以为 SQL 必然更简单而且更干净。根据所运行引擎的不同,SPARQL 可以全部通过称为链接数据结果(linked data effect)的内容进行分发。此外,它允许拥有动态模式,因为它拥有互相链接的面向对象的透视图,与严格的SQL 关系透视图形成对照。如果您想要把关系数据库表分隔为许多数据孤岛,则实际上使用的SQL代码行将比 SPARQL多很多 — 更不必说 SQL中会出现大量令人讨厌的JOIN 描述符。

SPARQL的前两行是PREFIX声明。根据语义 Web 理论,一切内容 — 无论是对象还是数据图来源(也是一个对象)— 都有统一资源标识符(Uniform Resource Identifier,URI)。PREFIX行只是将临时标签应用到一些 URI中 —在本例中为 Friend of a Friend和RDF 模式。其中的好处是您以后可以在查询中使用PREFIX声明而不必使用完整的URI。

SPARQL代码的下一行描述了查询请求。这条语句在本质上与 SQL语句相同,不同之处是对 URI的附加请求。注意问号的使用(?)是为了表示术语是变量。

FROM语句描述了获取数据的位置。这在SQL和SPARQL中是相同的,只是在SPARQL中,数据源名称是URI,而非表示计算机或网络中某个物理位置的字符串。

两者的WHERE语句完全不同,因为使用SPARQL,必须指定用于获取数据的模式。同样,如果尝试过使用关系方法执行此操作,则需要花费的代价比普通SQL多得多:需要使用PHP、Java™ 编程语言或者一些其他服务器端语言才能执行数据源之间的检查。SPARQL代码行完成的操作比较明了,这包括确保正在检索的数据只属于Person 类型。SPARQL将获取名称和位置,同时执行一些模式匹配以查找正确的John Smith。

创建

SPARQL中的CRUD操作通常比 read操作更神秘。但是,可以完成这些操作。首先,create操作将把新记录或对象插入到表或图表中。

清单 3. create 操作的 SQL 代码

INSERT
INTO UserTable (realname, dob, location)
VALUES ("John Smith", "1985-01-01", "Bristol, UK");

现在,比较清单3中基于SQL的代码与清单4中基于SPARQL的代码中的create操作。

清单 4. create 操作的 SPARQL 代码

PREFIX foaf:
<http://xmlns.com/foaf/0.1/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
INSERT
INTO GRAPH <http://www.example.com/graph>
(?realname, ?dob, ?location)
{
<http://www.example.org/graph/johnsmith#me> rdf:Type
foaf:Person ;
foaf:name "John Smith" ;
foaf:birthday
<1985-01-01T00:00:00> ;
foaf:location "Bristol, UK"
}

同样,注意 PREFIX行与用SPARQL在read操作中执行的操作完全相同。INSERT INTO的工作原理类似于SQL,但是同样,这是基于URI的,而非基于字符串、基于表和基于名称的,这允许使用HTTP 完成操作。您必须再次指定模式。在这里,比在read操作中了解起来略微简单一些,因为您几乎可以拥有任何属性,只要它与模式兼容。这就是RDF 所提供的分布式动态可扩展对象的优点。

删除

从某一程度来说,如果要创建,那很可能需要进行删除。例如,用户可能想要删除他们在您站点中的帐户(这显然是件遗憾的事情,他们想要离开,但是他们可能有正当的理由)。清单5提供了典型 delete操作的SQL代码。

清单 5. delete 操作的 SQL 代码

DELETE FROM UserTable
WHERE realname = "John Smith"

现在,比较清单5中基于SQL的代码与清单6中基于SPARQL的代码。

清单 6. delete 操作的 SPARQL 代码

DELETE
FROM GRAPH <http://www.example.com/graph>
{<http://www.example.org/graph/johnsmith#me> ?predicate ?object }

SQL 与 SPARQL 代码之间的基本差别是 SQL 删除了表中的某行,然而 SPARQL 将删除与 http://www.example.org/graph/johnsmith#me 所表示的 “John Smith” 相关的 “所有三元组”。这种差别是因为 RDF 模型是基于图表的。

更新

许多 Web 应用程序允许用户更新信息。UPDATE 操作是实现此功能的方法。清单 7 和 8 演示了如何在 SQL 和 SPARQL 中实现更新。

清单 7. update 操作的 SQL 代码

UPDATE UserTable
SET location = "Weston-super-Mare, UK"
WHERE realname = "Joanne Smith"

现在比较清单 7 中基于 SQL 的代码与清单 8 中基于 SPARQL 的代码中的 update 操作。

清单 8. update 操作的 SPARQL 代码

PREFIX foaf: <http://xmlns.com/foaf/0.1/>
MODIFY <http://www.example.org/graph>
DELETE {?uri foaf:location ?location}
INSERT {?uri foaf:location "Weston-super-Mare, UK"}
WHERE { ?uri foaf:name "Joanne Smith" }

SPARQL 中的 UPDATE 可能看上去非常愚蠢,但是当您知道它并不是要更新关系表行 — 而是要更新图表中的一个非常特定的关系时,它是完全有效的。实现更新而无需达到多个位置的最简单方法是执行删除和插入。MODIFY 关键字用于建立与正确图表的连接。

连接到 SQL 和 SPARQL 数据库系统

要执行上面的 SQL 和 SPARQL 语句,必须以某种方式连接到系统中。很明显,不同的系统有不同的连接方法。一种常见方法是使用开放数据库连接(Open Database Connectivity,ODBC)驱动程序连接到一般数据库,这些驱动程序通常附带于最新版本的 Mac OS X 和 Linux系统中,并且可以安装到诸如 Windows之类的其他操作系统中。ODBC 实际上提供了一个简单的通用 API,可以连接到所选的 SQL 数据库。有趣的是,ODBC 还可以与诸如 OpenLink Virtuoso 之类的一些语义 Web 数据库结合使用。但是,大多数其他语义 Web 数据库都需要一些可以处理诸如 Seseme 和 Jena 之类系统的自定义连接过程,或者自定义通用连接系统,比如 RDF2Go(在撰写本文时,RDF2Go 仅适用于 Java 技术)。如果数据将通过 HTTP 公开,那么另一种备选方法是基于 HTTP 的 SPARQL 连接,这种方法使您的数据成为 “链接数据” 并且可以实现完全分布。由于 SQL 和 SPARQL 的连接方法的范围是不同的,因此不适合在本文中详细介绍这些方法。

通过 PHP 实现 SQL 和 SPARQL

在选择连接方法后,通常下一个步骤是在 PHP 中执行常用操作。对于自定义系统,通常需要使用 SQL 字符串,插入 PHP 变量并通过函数参数传递。该函数随后将连接到数据库中并执行此事务。此处建议对 SPARQL-RDF 连接执行与 SQL-RDBMS 连接相同的操作。

因此,看一下另一种代码比较 — 这次使用 PHP 语言 — 从 read 开始,然后是 create、delete 和 update,同上面的代码比较一样。使用名为 query_execute 的虚构的查询执行函数,该函数将获取 SQL/SPARQL 语句的字符串表示。

读取

首先是简单的 read 操作。在 PHP 中可以使用字符串连接将变量插入到查询中。

清单 9. read 操作的 SQL 代码

function readUserInfo($realname) {
$sqlstatement = "SELECT realname, dob, location FROM UserTable WHERE
realname = \"" + realname + "\";";
return query_execute($sqlstatement);
}

现在,比较清单 9 中基于 SQL 的 PHP 代码与清单 10 中基于 SPARQL 的 PHP 代码中的 read 操作。

清单 10. read 操作的 SPARQL 代码

function readUserInfo($realname) {
$sqlstatement = "PREFIX foaf:
<http://xmlns.com/foaf/0.1/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT ?uri ?name ?dob ?location
FROM <http://www.example.org/graph>
WHERE
{ ?uri rdf:type foaf:Person ;
foaf:name \"" + $realname + "\" ;
foaf:birthday ?dob ;
foaf:location ?location .
} ;";
return query_execute($sparqlstatement);
}

正如您所见,以上函数在开发时使用函数名作为参数,这意味着您可以开始直接使用 SPARQL 版本。此外,如果使用的是 ODBC 系统,您不必担心返回类型发生变化。

创建

和上面一样。以下 PHP 方法将把变量插入到字符串中以运行动态查询。清单 11 显示了如何用 create 操作完成该动态查询。

清单 11. create 操作的 SQL 代码

function createUserInfo($realname, $dob,
$location) {
$sqlstatement = "INSERT INTO UserTable (realname, dob,
location) VALUES (\"" + $realname + "\", \"" + $dob + "\", \"" +
$location + "\");";
return query_execute($sqlstatement);
}

现在比较清单 11 中基于 SQL 的 PHP 代码与清单 12 中基于 SPARQL 的 PHP 代码中的 create 操作。

清单 12. create 操作的 SPARQL 代码

function createUserInfo($uri, $realname,
$dob, $location) {
$sparqlstatement = "PREFIX foaf:
<http://xmlns.com/foaf/0.1/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
INSERT INTO GRAPH <http://www.example.com/graph>
(?realname, ?dob, ?location)
{
" + $uri + " rdf:Type
foaf:Person ;
foaf:name \"" + $realname + "\" ;
foaf:birthday
<" + $dob + ">> ;
foaf:location \"" + $location +
"\}"
return query_execute($sparqlstatement);
}

为了简单起见,此函数的 SPARQL 版本将包括 URI 参数,但是,执行一些附加字符串连接使函数声明与 SQL 一致将非常简单。

删除

在 PHP 中,通过字符串连接使用插入操作来执行 delete 操作。请注意,如上一节所示,这些不同的变量传递到 SQL 和 SPARQL 版本中。清单 13 显示了 delete 操作的 SQL 代码。

清单 13. delete 操作的 SQL 代码

function deleteUserInfo($realname) {
$sqlstatement = "DELETE FROM UserTable WHERE realname = \"" + John Smith + "\"";
return query_execute($sqlstatement);
}

现在,比较清单 13 中基于 SQL 的 PHP 代码与清单 14 中基于 SPARQL 的 PHP 代码中的 delete 操作。

清单 14. delete 操作的 SPARQL 代码

function deleteUserInfo($uri) {
$sparqlstatement = "DELETE FROM GRAPH
<http://www.example.com/graph> {<" + $uri +
"> ?predicate ?object }";
return query_execute($sparqlstatement);
}

同样,为了简单起见,SPARQL 版本的函数将获取 URI,而非实际名称。您可以将 DELETE SPARQL 语句与 WHERE 子句结合使用以按名称而不是按 URI 查找数据,这将拥有与 SQL 版本相同的函数头。

更新

使用 PHP 在 SQL 和 SPARQL 中更新记录非常简单。不过要注意关系数据库与 RDF 提供的是不同的结构。清单 15 显示了 update 操作的 SQL 代码。

清单 15. update 操作的 SQL 代码

function updateUserInfo($realname, $location)
{
$sqlstatement = "UPDATE UserTable SET location = \"" + $location
+ "\" WHERE realname = \"" + $realname+ "\";";
return query_execute($sqlstatement);
}

现在比较清单 15 中基于 SQL 的 PHP 代码与清单 16 中基于 SPARQL 的 PHP 代码中的 update 操作。

清单 16. update 操作的 SPARQL

function updateUserInfo($realname,
$location) {
$sparqlstatement = " PREFIX foaf: <http://xmlns.com/foaf/0.1/>
MODIFY <http://www.example.org/graph>
DELETE {?uri foaf:location ?location}
INSERT {?uri foaf:location \"" + $location + "\"}
WHERE {
?uri foaf:name \"" + $realname + "\"
}
";
return query_execute($sparqlstatement);
}

 

 

 

SQL 与 SPARQL 版本中的函数声明是相同的。因此,从 SQL 转换到 SPARQL 十分简单。

虽然从使用 SQL 的数据源转移到使用 SPARQL 的数据源非常简单,但是有两个重要部分需要注意:抽象级别和 SQL 与 SPARQL 的异同。您应当避免产生这两种语言实际上相同的这种错误的想法。因此最好在了解一种语言的限制的同时,研究该语言的功能 — 特别是,不同语言提供的各种语法优点和图形表示。

抽象级别

本文中的示例代码与数据库结构紧密耦合。在理论上,在必须执行表结构重组之前,SQL 和 RDBMS 只能处理某种程度的松散耦合。然而,SPARQL 和 RDF 不会出现这个问题。使用 SPARQL 与数据进行耦合可能会非常抽象,因为 RDF 是分布式但又互相链接的。本文示例的可重用性可以通过提高抽象性来改进。但是,出于我们的目的,使用了简单的紧密耦合函数来说明 SPARQL 与 SQL 的异同。

正如您在示例代码中看到的,SQL 与 SPARQL 有许多相似性。不同之处在于 RDF 具有基于 Web 的、图形化和面向对象的特性以及如何将这些特性过滤到 SPARQL 语言中。作为一项简化的通用规则,您可以将 RDF 和 SPARQL 中的三元组结构作为基本表示,依次为行(主题)的惟一主键、属性/列名(谓词或关系)及基于行和列(对象)的单元格数据。此外,SPARQL 可以充分利用 HTTP 通信,并且因此使数据可以(但不是必须)基于内部网、外部网和更广阔的 Internet 进行分发。

为什么从 SQL 迁移到 SPARQL?

从 SQL 迁移到 SPARQL 有许多原因。详细原因超出了本文的讨论范围,但是您可以受到下面的启发:

需要一种分布更广泛的数据解决方案。

需要在 Web 上公开数据以供人们使用和链接。

您会发现 Node-Arc-Node 关系(三元组)比关系数据库模型更易于理解。

可能需要以纯面向对象的角度理解数据以结合使用 OOP 范例(PHP V5 及更高版本支持 OOP)。

需要构建可以在 Web 中连接到数据源的一般代理。

当然,不 希望从 SQL 迁移到 SPARQL 也有许多理由,并且这些理由可能非常充分。SPARQL 是另一种查询方法,不一定要立即替换 SQL。这同样适用于关系数据和基于语义 Web 的数据。并不是说要替换,相反,最好结合使用新旧技术,从而生成一种混合系统,它可以处理旧的遗留系统、现行系统和未来系统,同时也可以被这些系统处理。

Tell others about me!

Leave a Reply

Your email address will not be published. Required fields are marked *