前面幾篇已經(jīng)介紹了本體構(gòu)建模塊廓脆,運(yùn)行本體構(gòu)建模塊可以構(gòu)建出一個(gè)初步的本體庫宛瞄。
在構(gòu)建出本體庫之后,我們該如何對它進(jìn)行操作呢肝匆?
這一篇介紹本體查詢模塊。
SPARQL
大多數(shù)程序員在日常開發(fā)中接觸最多的應(yīng)當(dāng)是關(guān)系型數(shù)據(jù)庫顺献,例如 MySQL、Oracle枯怖、SQL Server 等注整。這些關(guān)系型數(shù)據(jù)庫使用標(biāo)準(zhǔn)的結(jié)構(gòu)化查詢語言 SQL 來實(shí)現(xiàn)對數(shù)據(jù)的查詢和更新等操作。
雖然不同數(shù)據(jù)庫以及同一數(shù)據(jù)庫不同版本對 SQL 語言規(guī)范的支持和實(shí)現(xiàn)都有所不同度硝,不同數(shù)據(jù)庫在 SQL 上的語法也會有細(xì)微的差別肿轨。但是 SQL 語言已經(jīng)極大降低了數(shù)據(jù)庫的學(xué)習(xí)成本,其非過程化蕊程、自然語言風(fēng)格等特點(diǎn)極大提高了我們操作關(guān)系型數(shù)據(jù)庫的效率椒袍。
從關(guān)系型數(shù)據(jù)庫回到我們的本體庫(RDF數(shù)據(jù)庫),自然很容易聯(lián)想到針對采用 RDF 框架的數(shù)據(jù)庫也應(yīng)該有一套相應(yīng)的查詢操作語言藻茂。
SPARQL(全稱 SPARQL Protocol and RDF Query Language) 就是我們需要的針對 RDF 的查詢語言驹暑,它提供了查詢 RDF 數(shù)據(jù)庫的能力玫恳,并在 2008 正式成為 W3C 的推薦標(biāo)準(zhǔn)語言。2013 年更新至 SPARQL 1.1优俘,SPARQL 1.1 提供了更為強(qiáng)大的能力京办,添加了例如更新、查詢嵌套等操作帆焕。
小栗子
假如現(xiàn)在本體庫中存有如下三元組:
美人魚 有導(dǎo)演 周星馳
現(xiàn)在如果想要查詢 "美人魚的導(dǎo)演是誰"惭婿,那么可以通過如下代碼實(shí)現(xiàn):
SELECT ?name
WHERE {
美人魚 有導(dǎo)演 ?name
}
其中 SELECT 和 WHERE 不必多說,值得注意的是 WHERE 子句中的內(nèi)容叶雹。之前已經(jīng)提到了資源描述框架中 RDF 中數(shù)據(jù)的表達(dá)單位是一個(gè)三元組——(subject,predicate,object)财饥,即主體-謂語-客體。因此在進(jìn)行查詢時(shí)也以三元組的形式給定查詢條件折晦,在查詢條件中钥星,三元組中三元素的任意一個(gè)都可以設(shè)定為變量。
例如現(xiàn)在如果要查詢周星馳導(dǎo)演過哪些電影筋遭,可以這些設(shè)定變量:
SELECT ?movie
WHERE {
?movie 有導(dǎo)演 周星馳
}
以上代碼就可以列出當(dāng)前本體庫中存有的所有周星馳導(dǎo)演的作品打颤。
再進(jìn)一步如果想要查詢美人魚導(dǎo)演的年齡,可以實(shí)現(xiàn)如下:
SELECT ?age
WHERE {
美人魚 有導(dǎo)演 ?a.
?a 有年齡 ?age.
}
從中可以看到通過相同變量來標(biāo)識同一實(shí)體漓滔,這樣可實(shí)現(xiàn)連接的效果编饺,查詢可表示為"美人魚 -> 導(dǎo)演"、"導(dǎo)演 -> 年齡"响驴,連接為 "美人魚 -> 導(dǎo)演 -> 年齡"透且。
當(dāng)然以上的查詢語句是一種簡化,是為了方便表達(dá) SPARQL 的基本含義豁鲤。再考慮上語法細(xì)節(jié)秽誊,應(yīng)該實(shí)現(xiàn)如下:
PREFIX mymo: <http://www.semanticweb.org/narutoku/ontologies/2016/3/my-ontology#>
SELECT ?name
WHERE {
mymo:美人魚 mymo:有導(dǎo)演 ?name
}
之前提到過本體應(yīng)該具有共享的特點(diǎn),而且 RDF 很重要的一個(gè)應(yīng)用就是語義網(wǎng)琳骡。
因此我們構(gòu)建的本體庫應(yīng)該要考慮到與他人共享锅论、避免沖突等問題,這就需要為我們本體庫中的實(shí)體以及關(guān)系等設(shè)定唯一標(biāo)識楣号。我們的 "美人魚" 實(shí)體應(yīng)該表達(dá)為最易,"標(biāo)識: 美人魚", 在 Answer 系統(tǒng)中這個(gè)標(biāo)識就是 <http://www.semanticweb.org/narutoku/ontologies/2016/3/my-ontology#>
炫狱。
所以在構(gòu)造 SPARQL 查詢語句的時(shí)候需要在每個(gè)實(shí)體和關(guān)系前面添加 <http://www.semanticweb.org/narutoku/ontologies/2016/3/my-ontology#>
藻懒,為了方便可以使用 PREFIX mymo : <標(biāo)識> 簡化,如上代碼所示视译。
另外在系統(tǒng)實(shí)際代碼中是不會直接用 "美人魚" 來查詢的嬉荆,因?yàn)橹耙约疤岬竭^ "美人魚" 存在同名實(shí)體和實(shí)體別名的情況,所以最終會使用實(shí)體 ID(在構(gòu)建本體庫生成 UUID 作為實(shí)體或關(guān)系的唯一標(biāo)識)來查詢酷含。而同名實(shí)體的識別或?qū)嶓w別名的消歧在構(gòu)建查詢語句之前完成鄙早,這一部分會在語義解析模塊中介紹汪茧。
以上是對 SPARQL 極短的介紹,畢竟 SPARQL 語言不是一篇博客就能介紹的完的蝶锋。文章的末尾給出了一些關(guān)于 SPARQL 的參考資料陆爽。
使用 Jena
上面已經(jīng)介紹了 RDF 數(shù)據(jù)查詢語言 SPARQL。雖然 SPARQL 和 SQL 一樣具有易學(xué)易用的特點(diǎn)扳缕,是非常強(qiáng)大的工具慌闭。但是如果在每一次的應(yīng)用開發(fā)中,針對每一次的數(shù)據(jù)查詢需求都要反反復(fù)復(fù)編寫 SPARQL 語句還是顯得有點(diǎn)麻煩躯舔,尤其是在一些簡單但又頻繁使用的操作場景驴剔,例如添加一個(gè)實(shí)體、或添加一個(gè)屬性粥庄、又或向一個(gè)實(shí)體添加一個(gè)數(shù)據(jù)屬性等等丧失,這些操作需求頻繁出現(xiàn),重復(fù)編寫 SPARQL 語句十分不必要惜互。
這時(shí)候就輪到 Jena 出場了布讹。Jena 是 Apache 軟件基金會開源的 RDF 數(shù)據(jù)查詢框架,它是對 SPARQL 語言規(guī)范的實(shí)現(xiàn)训堆,是對 SPARQL 一些基本操作的封裝描验。利用它提供的豐富的 API 就可以實(shí)現(xiàn)對本體庫進(jìn)行各種操作。
例如如果想要添加一個(gè)實(shí)體坑鱼,可使用:
/**
* 創(chuàng)建實(shí)體
* 根據(jù) UUID 和所屬類 創(chuàng)建一個(gè)實(shí)體
* @param individualId 實(shí)體 ID
* @param genusClass 實(shí)體類型
* @return
*/
public Individual createIndividual(String individualId, OntClass genusClass);
給一個(gè)實(shí)體添加數(shù)據(jù)屬性:
/**
* 給實(shí)體添加單個(gè)數(shù)據(jù)屬性
* @param ontologyClass
* @param individualId 實(shí)體標(biāo)識
* @param propertyName 屬性名
* @param propertyValue 屬性值
* @return
*/
public boolean addDataProperty(Individual individual, String propertyName, String propertyValue);
查詢實(shí)體是否存在:
/**
* 查詢實(shí)體是否存在
* @param individualName
* @return
*/
public boolean individualExist(String individualName);
Jena 還提供了其它非常多功能強(qiáng)大的 API膘流,更多更詳細(xì)的 API 可查詢 官方 API 文檔。
Jena 官方站點(diǎn) 也給出了 Jena 的教程等相關(guān)參考資料鲁沥。
數(shù)據(jù)訪問封裝
有了 SPARQL 和 Jena 之后呼股,我們就可以對本體庫進(jìn)行基本的操作。有了這些基本操作画恰,我們就可以在這之上對業(yè)務(wù)層面的數(shù)據(jù)訪問需求進(jìn)行封裝彭谁。
比如在語義解析模塊中進(jìn)行實(shí)體別名消歧(星爺-消歧成-周星馳)的時(shí)候就需要查詢本體庫中的等價(jià)實(shí)體——查詢 "星爺" 是否有等價(jià)實(shí)體,查詢本體庫發(fā)現(xiàn) "星爺" 等價(jià)實(shí)體 "周星馳"允扇,則返回 "周星馳")缠局。我們就可以將這個(gè)同名實(shí)體的數(shù)據(jù)查詢需求做一個(gè)封裝。
所謂本體查詢模塊就是對 Answer 系統(tǒng)中諸如上述 "查詢等價(jià)實(shí)體" 的數(shù)據(jù)訪問需求的封裝蔼两。
以 "查詢等價(jià)實(shí)體" 為例,代碼如下:
public String querySameIndividual(String individualName) { // individualName 表示實(shí)體的標(biāo)識
String sameIndividual = null; // 返回的等價(jià)實(shí)體結(jié)果
String prefix = "prefix mymo: <" + Config.pizzaNs + ">\n" +
"prefix rdfs: <" + RDFS.getURI() + ">\n" +
"prefix owl: <" + OWL.getURI() + ">\n"; // 查詢前綴
String QL = "SELECT ?等價(jià)實(shí)體 WHERE {?等價(jià)實(shí)體 owl:sameAs mymo:" + individualName + ".\n}"; // 查詢等價(jià)實(shí)體的 SPARQL 語句逞度,其中等價(jià)實(shí)體的關(guān)系可以用 owl:sameAs 表達(dá)
String SPARQL = prefix + QL; // 添加好前綴额划,構(gòu)建完整查詢語句
// 使用 Jena API 構(gòu)建好查詢對象
Query query = QueryFactory.create(SPARQL);
QueryExecution qexec = QueryExecutionFactory.create(query, model);
ResultSet results = qexec.execSelect();// 執(zhí)行查詢
ResultSetRewindable resultSetRewindable = ResultSetFactory.makeRewindable(results);
int numCols = resultSetRewindable.getResultVars().size();
while (resultSetRewindable.hasNext()) { // 遍歷所有查詢結(jié)果
QuerySolution querySolution = resultSetRewindable.next();
for (int col = 0; col < numCols;col++) {
String rVar = results.getResultVars().get(col);
RDFNode obj = querySolution.get(rVar);
sameIndividual = FmtUtils.stringForRDFNode(obj).split(":")[1]; // 獲取查詢結(jié)果-等價(jià)實(shí)體
}
}
return sameIndividual; // 返回查詢結(jié)果
}
除了 "查詢等價(jià)實(shí)體" 之外,還封裝了其它一些簡單操作档泽,例如查詢所有以 Subject 為主體的三元組:
/**
* 查詢所有以 Subject 為主體的三元組(這里稱之為斷言)
* 代碼還有待優(yōu)化
*/
@Override
public List<Statement> getStatementsBySubject(String subject) {
List<Statement> statements = new ArrayList<Statement>();
StmtIterator stmtIter = model.listStatements(); // 列出所有的三元組
while(stmtIter.hasNext()) { // 循環(huán)所有斷言
Statement statement = stmtIter.next();
String subjectName = null;
if (statement.getSubject() != null && statement.getSubject().getURI() != null) {
String[] urlFields = statement.getSubject().getURI().split("#");
if (urlFields.length > 1) {
subjectName = urlFields[1];
} else {
subjectName =urlFields[0];
}
if (subjectName != null) {
if (subjectName.equals(subject)) { // 尋找主體為參數(shù) subject 的三元組
statements.add(statement);
}
}
}
}
return statements; // 返回最終結(jié)果
}
如上所述俊戳,本體查詢模塊就是對一些本體操作的封裝揖赴,是系統(tǒng)的數(shù)據(jù)訪問層。
到目前為止抑胎,本體查詢模塊封裝的都是一些非常簡單的操作燥滑,因此實(shí)際上是可以不單獨(dú)構(gòu)造一個(gè)模塊的。現(xiàn)在單獨(dú)劃分一個(gè)模塊阿逃,是為了方便之后的系統(tǒng)重構(gòu)铭拧,以后如果實(shí)現(xiàn)了本體的分布式存儲后者添加一些其它數(shù)據(jù)源如圖數(shù)據(jù)庫,那么只需要修改本體查詢模塊并可恃锉。
相關(guān)資料
SQARQL
下一篇
下一篇開始將介紹語義理解模塊搀菩,語義理解模塊的主要作用是解析自然語言,使用查詢語義圖表達(dá)用戶的查詢語義破托,通過實(shí)體消歧肪跋、謂語消歧等操作提高語義理解的程度,最終通過本體查詢模塊查詢答案土砂。
汪
汪.