Virtuoso(OSS版) dockerを立ち上げる

# Virtuoso(OSS版) dockerを立ち上げる。

RDFとSPARQLの勉強のために、SPARQLサーバの設定を行ったときのメモ。
VirtuosoはOPENLINK SOFTWARE社製のSPARQLサーバで、割と人気があるようなのでこれを使用する。
dockerにもいくつか環境が登録されているようだ。OFFICIALのイメージは無い。

環境は CentOS 7.3.1611、docker-1.12.6

## 環境構築

### イメージの取得

後から、同じ環境を再現するために、基本、タグ付きでイメージを取得する。

```
$ docker search Virtuoso
INDEX       NAME                                  DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
docker.io   docker.io/tenforce/virtuoso           Docker for hosting Virtuoso.                    27                   [OK]
docker.io   docker.io/tenforce/ods-virtuoso       RDF triple store with 15 catalogs in dcat-ap    5                    [OK]
docker.io   docker.io/inutano/virtuoso            Virtuoso Opensource                             3                    [OK]
docker.io   docker.io/nicholsn/virtuoso                                                           3                    [OK]
docker.io   docker.io/stain/virtuoso              Virtuoso 7 (stable) Open Source Edition on...   2                    [OK]
$ docker pull tenforce/virtuoso:1.1.1-virtuoso7.2.4
Digest: sha256:759e0de994078f3a59d32e1e868892d7a0e643b4452ef5f964bafe1fa5edae63
$ docker images
REPOSITORY                         TAG                   IMAGE ID            CREATED             SIZE
docker.io/tenforce/virtuoso        1.1.1-virtuoso7.2.4   37254352698c        4 weeks ago         502.5 MB
```    

### Virtuoso起動

永続のための領域をVOLUME上に確保し、Virtuosoを起動する。起動オプションは[Dockerのページ](https://hub.docker.com/r/tenforce/virtuoso/)
に記述されているものを参考にする。

```
$ docker volume create --name main-virtuoso-data
main-virtuoso-data
$ docker volume ls
DRIVER              VOLUME NAME
local               main-virtuoso-data
$ docker run -d --restart=always --name main-virtuoso \
    -p 8890:8890 -p 1111:1111 \
    -e DBA_PASSWORD=dba \
    -e SPARQL_UPDATE=true \
    -e DEFAULT_GRAPH=http://www.example.com/graph \
    -v main-virtuoso-data:/data \
    -d tenforce/virtuoso:1.1.1-virtuoso7.2.4
7f73ea2c9f38bb6c36b7ee56032d2647ee13522580ed716907a3bbb555eb2632
$ docker ps
CONTAINER ID        IMAGE                                   COMMAND                  CREATED             STATUS              PORTS                                            NAMES
7f73ea2c9f38        tenforce/virtuoso:1.1.1-virtuoso7.2.4   "/bin/bash /virtuoso."   15 seconds ago      Up 14 seconds       0.0.0.0:1111->1111/tcp, 0.0.0.0:8890->8890/tcp   main-virtuoso
```

1111 がコマンドライン(isql-vt)用ポート
8890 がWebインタフェース用ポート番号

CentOS上のVirtuoso http://192.168.45.131:8890/ にアクセス。CentOSにはGUIをインストールしていないため、他よりアクセスする。

![VIRTUOSO Web画面.png](https://qiita-image-store.s3.amazonaws.com/0/137664/4f940c49-a511-c488-da1a-26b73b144483.png)

### ホスト側のツールインストール

ホスト側にCUIツールをインストールする。これにより、CUIコマンド(isql-vt)が利用可能になる。

```
$ sudo yum install virtuoso-opensource-utils
```

---
## RDFデータの登録方法

### GUIからのデータ登録

[VirtuosoにRDFデータをロード，SPARQLで検索](http://d.hatena.ne.jp/keybot/20110904/p1)を参考にしてデータを登録してみる。

http://192.168.45.131:8890/conductor/ にアクセスし、デフォルトのアカウント dba でログイン、パスワードはdocker起動時に指定したもの dba を指定する。
ログイン後、[Linked Data] – [Quad Store Upload]からデータのアップロード画面を開く。

![virtuoso-conductor.png](https://qiita-image-store.s3.amazonaws.com/0/137664/a53751b0-ba01-4d25-2ed6-d6731f37023e.png)

> 参考にしたページはvirtuosoのバージョンが6.x系のためか「RDF」となっているが、使用するイメージは7.2.4では「Linked Data」となっている。

![vtirtuoso-quad_store_upload.png](https://qiita-image-store.s3.amazonaws.com/0/137664/da6532d1-4111-0e18-6fa5-27adaf06b283.png)

### CUIからのデータ登録

DBPedia Japanが公開している[データ](http://ja.dbpedia.org/dumps/)のなかから'20160407/jawiki-20160407-article-categories.ttl.bz2'を投入してみる。

読み込むテキストのフォーマットにより、パーサーを指定する必要がある。

  * [TTLP_MT](http://docs.openlinksw.com/virtuoso/fn_ttlp_mt/) ……… TTL (TURTLE), TTL(N-Triple) or NQ (N-quad)をパース
  * [RDF_LOAD_RDFXML_MT](http://docs.openlinksw.com/virtuoso/fn_rdf_load_rdfxml_mt/) ……… RDF/XMLテキストをRDFトリプルのシーケンスとしてパース

ホスト上で読み込みコマンドを実行すると、以下のようなエラーになる。

```
$ isql-vt -U dba -P dba
SQL> DB.DBA.TTLP_MT (file_to_string_output ('jawiki-20160407-article-categories.ttl'), '', 'http://example.com/', 512);

*** Error 42000: [Virtuoso Driver][Virtuoso Server]FA112: Can't stat file 'jawiki-20160407-article-categories.ttl', error (2) : No such file or directory
```

straceコマンドでdocker上のVirstuosoサーバとクライアントコマンドをシステムコールをチェックすると、
TTLファイルは、docker上のVirstuosoサーバ側が参照しているため、ホストに配置したTTLファイルは見つからないのエラーとなる。

 docker上にファイルを配置して、TTLファイルの読み込みを行う。

```
$ docker cp jawiki-20160407-article-categories.ttl main-virtuoso:/data/.
$ isql-vt -U dba -P dba
DB.DBA.TTLP_MT (file_to_string_output ('/data/jawiki-20160407-article-categories.ttl'), '', 'http://example.com/', 512);

*** Error 42000: [Virtuoso Driver][Virtuoso Server]FA003: Access to '/data/jawiki-20160407-article-categories.ttl' is denied due to access control in ini file
```

が、エラーとなる。どうやら読み込みを許可しているパスは、virstuoso.intファイル内で決まっているようだ。

```text:/usr/local/virtuoso-opensource/var/lib/virtuoso/db/virtuoso.ini
DirsAllowed                     = ., /usr/local/virtuoso-opensource/share/virtuoso/vad
```

TTLファイルを許可されたディレクトリに移動し、再度読み込みを行う。

```
DB.DBA.TTLP_MT (file_to_string_output ('/usr/local/virtuoso-opensource/share/virtuoso/vad/jawiki-20160407-article-categories.ttl'), '', 'http://example.com/', 512);
Done. -- 74800 msec.
```

---
## RDFデータの検索

### GUIから登録したデータの検索

[Linked Data] – [SPARQL]からSPARQLクエリを入力し、「Execute」を押下し検索を実行する。

  ```
select ?o where {
   <http://ja.dbpedia.org/resource/高橋留美子> ?p ?o
}
  ```

![vtirtuoso-SPARQL.png](https://qiita-image-store.s3.amazonaws.com/0/137664/3f461743-3ab5-550d-73dc-ba65a958c22b.png)

ブラウザより検索を実行。正常にロードできている。

### CUIからデータの検索

ホストにインストールしたCUIコマンド(isql-vt)を使用してSPARQLクエリを実行する。

```
$ isql-vt -U dba -P dba
SQL> sparql select ?o where { <http://ja.dbpedia.org/resource/高橋留美子> ?p ?o } ;
o
LONG VARCHAR
_______________________________________________________________________________

http://ja.dbpedia.org/resource/Category:1957年生
http://ja.dbpedia.org/resource/Category:存命人物
http://ja.dbpedia.org/resource/Category:新潟市出身の人物
http://ja.dbpedia.org/resource/Category:日本の漫画家
http://ja.dbpedia.org/resource/Category:日本女子大学出身の人物
http://ja.dbpedia.org/resource/Category:高橋留美子

6 Rows. -- 58 msec.
SQL>
```

### WebAPIからの検索

  アプリから利用するためには、RESTかSOAPのやり取りを行う必要がある。

[Virtuoso SPARQL Query Service](https://virtuoso.openlinksw.com/dataspace/doc/dav/wiki/Main/VOSSparqlProtocol)では、以下のように記載があるのでPOSTで試す。

|Method|Notes|
|------|-----|
|GET|Short queries are sent in GET mode|
|POST|Queries longer than 1900 bytes are POST-ed|


[ここ](http://docs.openlinksw.com/virtuoso/soaprequestoversparqlendpoint/)にSOAPのサンプルが載っていたので試す。

```xml:soap.xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <query-request xmlns="http://www.w3.org/2005/09/sparql-protocol-types/#">
      <query xmlns="">
        SELECT DISTINCT ?z WHERE { &lt;http://ja.dbpedia.org/resource/高橋留美子&gt; ?y ?z .} LIMIT 10
      </query>
    </query-request>
  </soapenv:Body>
</soapenv:Envelope>
```

```
$ curl -d@soap.xml -H "Content-Type:text/xml" -H "SOAPAction: ''" http://192.168.45.131:8890/sparql/
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<query-result xmlns="http://www.w3.org/2005/09/sparql-protocol-types/#">
<sparql xmlns="http://www.w3.org/2005/sparql-results#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3.org/2001/sw/DataAccess/rf1/result2.xsd">
 <head>
  <variable name="z"/>
 </head>
 <results distinct="false" ordered="true">
  <result>
   <binding name="z"><uri>http://ja.dbpedia.org/resource/Category:1957年生</uri></binding>
  </result>
  <result>
   <binding name="z"><uri>http://ja.dbpedia.org/resource/Category:存命人物</uri></binding>
  </result>
  <result>
   <binding name="z"><uri>http://ja.dbpedia.org/resource/Category:新潟市出身の人物</uri></binding>
  </result>
  <result>
   <binding name="z"><uri>http://ja.dbpedia.org/resource/Category:日本の漫画家</uri></binding>
  </result>
  <result>
   <binding name="z"><uri>http://ja.dbpedia.org/resource/Category:日本女子大学出身の人物</uri></binding>
  </result>
  <result>
   <binding name="z"><uri>http://ja.dbpedia.org/resource/Category:高橋留美子</uri></binding>
  </result>
 </results>
</sparql></query-result></soapenv:Body></soapenv:Envelope>
```

[SPARQL Guide for the Javascript Developer](https://www.openlinksw.com/blog/~kidehen/?id=1653) を見てみるとXMLでなくてもいけそうなので試してみる。
ただし、日本語の応答がエンコード(¥uXXXX)されているのでデコードする必要があった。

```text:url.txt
query=SELECT+DISTINCT+%3Fo+WHERE+%7B+%3Chttp%3A%2F%2Fja.dbpedia.org%2Fresource%2F%E9%AB%98%E6%A9%8B%E7%95%99%E7%BE%8E%E5%AD%90%3E+%3Fp+%3Fo%7D&format=application%2Fjson
```

```
$ curl -s -d@url.txt -H "Content-Type: application/x-www-form-urlencoded" 'http://192.168.45.131:8890/sparql/' | perl -Xpne 's/\\u([0-9a-fA-F]{4})/chr(hex($1))/eg'
{ "head": { "link": [], "vars": ["o"] },
  "results": { "distinct": false, "ordered": true, "bindings": [
    { "o": { "type": "uri", "value": "http://ja.dbpedia.org/resource/Category:1957年生" }},
    { "o": { "type": "uri", "value": "http://ja.dbpedia.org/resource/Category:存命人物" }},
    { "o": { "type": "uri", "value": "http://ja.dbpedia.org/resource/Category:新潟市出身の人物" }},
    { "o": { "type": "uri", "value": "http://ja.dbpedia.org/resource/Category:日本の漫画家" }},
    { "o": { "type": "uri", "value": "http://ja.dbpedia.org/resource/Category:日本女子大学出身の人物" }},
    { "o": { "type": "uri", "value": "http://ja.dbpedia.org/resource/Category:高橋留美子" }} ] } }
```

後は、ブラウザ上のjavascriptアプリから検索できることを確認したい。

## 参考

 * [VirtuosoにRDFデータをロード，SPARQLで検索Add Star](http://d.hatena.ne.jp/keybot/20110904/p1)
 * [Virtuosoへのデータのロード方法(英語)](https://virtuoso.openlinksw.com/dataspace/doc/dav/wiki/Main/VirtRDFInsert)
 * [Virtuosoを試す](http://qiita.com/ikuo/items/4681e52cf3112144f146)
 * [How to Install Virtuoso Open Source (VOS) on CentOS Linux](https://www.openlinksw.com/dataspace/doc/dav/wiki/Main/VOSCentosNotes)
 * [Virtuosoを試す](http://qiita.com/ikuo/items/4681e52cf3112144f146)
 * [SPARQLthon19/TripleLoad](http://wiki.lifesciencedb.jp/mw/SPARQLthon19/TripleLoad#TTL.E3.81.AE.E3.83.AD.E3.83.BC.E3.83.89.28DB.DBA.TTLP_MT.29)
 * [Virtuoso SPARQL Query Service](https://virtuoso.openlinksw.com/dataspace/doc/dav/wiki/Main/VOSSparqlProtocol)
 * [VirtuosoのCross-Origin Resource Sharing (CORS) 許可](http://qiita.com/misshie@github/items/f603bf5a3971ab576907)
 * [Cross-Origin Resource Sharing (CORS) enabling a Virtuoso SPARQL Endpoint](http://vos.openlinksw.com/owiki/wiki/VOS/VirtTipsAndTricksCORsEnableSPARQLURLs)