SPARQLプロトコル

# 背景
世の中にはWeb上に散らばるデータを関連付けるための[Linked Data](http://linkeddata.org/)という概念があります。リンク付されたデータを表すためのフォーマットに[RDF](https://www.w3.org/RDF/)があります。RDFを検索するための言語として[SPARQL](https://www.w3.org/TR/rdf-sparql-query/)があります。そしてSPARQLで問い合わせ可能なデータベースとして`SPARQL Endpoint`があります。

# SPARQLプロトコル

ここではSPARQLリクエストをSPARQL EndpoinにHTTPで送る方法を整理します。

SPARQLクエリの文法、レスポンスフォーマットの内容については扱いません。送信するSPARQLはSPARQL Endpointに登録されているラベルの数を取得する次のクエリとします。

```sql
SELECT (COUNT(?l) AS ?count)
WHERE { 
  ?x <http://www.w3.org/2000/01/rdf-schema#label> ?l . 
}
```

SPARQLクエリは、SQLクエリに似ています。

## リクエスト

HTTPリクエストの送信方法は[SPARQL 1.1 Protocol - 2.1 query operation](https://www.w3.org/TR/sparql11-protocol/#query-operation)で定義されています。

HTTPメソッドとしてGETまたはPOSTが選択できます。POSTメソッドではSPARQLクエリの指定方法が二つあります。

Endpointによってサポートしているリクエスト形式はまちまちです。例えば `http://bio2rdf.org/sparql` というURLのSPARQLエンドポイントはGETメソッドのみに対応しています。

### GETメソッド
GETメソッドではHTTPリクエストのクエリパラメータでSPARQLクエリを指定します。パラメータ名は`query`です。

例えば、curlコマンドで、`http://ep.lodqa.org/qald-biomed/query`というURLのEndpointにSPARQLクエリを投げるには次のようにしていします。

```sh
curl 'http://ep.lodqa.org/qald-biomed/query' -XGET -d query='SELECT (COUNT(?l) AS ?count) WHERE { ?x <http://www.w3.org/2000/01/rdf-schema#label> ?l . }'
```

この時、SPARQLクエリ文字列をURLエンコードする必要があります。上記のcurlコマンドの例ではcurlコマンドが`-d`オプションの引数を自動的にURLエンコードしてくれています。自分でURL文字列をつくる際には次のようにURLエンコードする必要があります。

```http
http://ep.lodqa.org/qald-biomed/query?query=SELECT%20(COUNT(%3Fl)%20AS%20%3Fcount)%20WHERE%20%7B%20%3Fx%20%3Chttp%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23label%3E%20%3Fl%20.%20%7D
```

エンコードせずにSPARQLクエリを送信した場合、curlでは次のように`400 Bad Request`レスポンスになります。

```
~ curl 'http://ep.lodqa.org/qald-biomed/query?query=SELECT (COUNT(?l) AS ?count) WHERE { ?x <http://www.w3.org/2000/01/rdf-schema#label> ?l . }' -v
*   Trying 133.39.78.111...
* TCP_NODELAY set
* Connected to ep.lodqa.org (133.39.78.111) port 80 (#0)
> GET /qald-biomed/query?query=SELECT (COUNT(?l) AS ?count) WHERE  ?x <http://www.w3.org/2000/01/rdf-schema HTTP/1.1
> Host: ep.lodqa.org
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 400 Bad Request
< Date: Thu, 01 Nov 2018 06:19:40 GMT
< Server: Apache
< Content-Length: 226
< Connection: close
< Content-Type: text/html; charset=iso-8859-1
<
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
</body></html>
* Closing connection 0
```

ブラウザでエンコードされていないURL `http://ep.lodqa.org/qald-biomed/query?query=SELECT (COUNT(?l) AS ?count) WHERE { ?x <http://www.w3.org/2000/01/rdf-schema#label> ?l . }` を開いた場合は、ブラザによって挙動が異なる可能性がありますが、Google ChromeやFirefoxでは次のメッセージを表示します。

```json
{"message":"Encountered \" \"<\" \"< \"\" at line 1, column 41.\nWas expecting one of:\n    \"(\" ...\n    \"!\" ...\n    \"^\" ...\n    \"a\" ...\n    <Q_IRI_REF> ...\n    <PNAME_NS> ...\n    <PNAME_LN> ...\n    <VAR1> ...\n    <VAR2> ...\n    ","code":"MalformedQuery"}
```

大意としては、SPARQLクエリの形式が不正である意味のエラーメッセージです。

### POSTメソッド
POSTメソッドではSPARQLクエリはHTTPリクエストのBODYに格納します。BODYの形式には2種類あります。

#### x-www-form-urlencoded

ブラウザのフォームからHTTPリクエストを送信する際の形式です。CURLコマンドの例です。

```sh
curl 'http://ep.lodqa.org/qald-biomed/query' -d query='SELECT (COUNT(?l) AS ?count) WHERE { ?x <http://www.w3.org/2000/01/rdf-schema#label> ?l . }'
```

`-XGET`をなくしただけで、あとは一緒です。curlコマンドは`-d`オプションを指定された場合のデフォルトのHTTPメソッドはPOSTです。またエンコード方式は`application/x-www-form-urlencoded`です。

前述したように、`application/x-www-form-urlencoded`はブラウザのフォームからHTTPリクエストを送信するときの形式です。次のようなHTMLフォームでSPARQLが実行できます。

```html
<form action="http://ep.lodqa.org/qald-biomed/query">
  <input name="query" value="SELECT (COUNT(?l) AS ?count) WHERE { ?x <http://www.w3.org/2000/01/rdf-schema#label> ?l . }">
  <button>実行</button>
</form>
```

このときもSPARQLクエリを格納するパラメータ名は`query`です。


#### sparql-query

もうひとつのBODYの形式では、BODYに直接SPARQLクエリを書くことができます。curlの例です。

```sh
curl 'http://ep.lodqa.org/qald-biomed/query' -H 'Content-Type: application/sparql-query' -d 'SELECT (COUNT(?l) AS ?count) WHERE { ?x <http://www.w3.org/2000/01/rdf-schema#label> ?l . }'
```

この時BODYの形式を明示するためにHTTPヘッダに`Content-Type: application/sparql-query`を指定する必要があります。またBODYの内容はSPARQL文字列だけす。`query=`は不要です。

おもにプログラムからSPARQLクエリを実行する際に使います。Rubyの例です。

```rb
Net::HTTP.post URI('http://ep.lodqa.org/qald-biomed/query'),
               'SELECT (COUNT(?l) AS ?count) WHERE { ?x <http://www.w3.org/2000/01/rdf-schema#label> ?l . }',
               'content-type' => 'application/sparql-query'
```

BODYにSPARQLクエリを文字列のまま指定できるので便利です。

## レスポンスフォーマット

JSONとXMLを指定することができます。

例えば先程までの`http://ep.lodqa.org/qald-biomed/query`へのリクエストでは、次のようなXML形式のレスポンスが帰ってきます。

```xml
<?xml version='1.0' encoding='UTF-8'?>
<sparql xmlns='http://www.w3.org/2005/sparql-results#'>
	<head>
		<variable name='count'/>
	</head>
	<results>
		<result>
			<binding name='count'>
				<literal datatype='http://www.w3.org/2001/XMLSchema#integer'>30389</literal>
			</binding>
		</result>
	</results>
</sparql>
```


### JSON
JSONレスポンスフォーマットの指定方法は[SPARQL 1.1 Query Results JSON Format - 6 Internet Media Type, File Extension and Macintosh File Type
](https://www.w3.org/TR/2013/REC-sparql11-results-json-20130321/#content-type)で定義されています。

HTTPリクエストのHTTPヘッダに`Accept: application/sparql-results+json`を指定すると、レスポンスをJSON形式で取得できます。curlの例です。

```sh
curl 'http://ep.lodqa.org/qald-biomed/query' -H 'Content-Type: application/sparql-query' -H 'Accept: application/sparql-results+json' -d 'SELECT (COUNT(?l) AS ?count) WHERE { ?x <http://www.w3.org/2000/01/rdf-schema#label> ?l . }'
```

次のようにレスポンスはJSON形式になります。

```json
{
  "head" : {
    "vars" : [
      "count"
    ]
  },
  "results" : {
    "bindings" : [
      {
        "count" : {
          "datatype" : "http://www.w3.org/2001/XMLSchema#integer",
          "type" : "literal",
          "value" : "30389"
        }
      }
    ]
  }
}
```

最近のプログラミング言語ではJSONのサポートが手厚いので、JSON形式が嬉しい場合があると思います。

### XML

また明示的にXMLフォーマットを指定することもできます。
XMLレスポンスフォーマットの指定方法は[SPARQL Query Results XML Format (Second Edition)
- 5. Internet Media Type, File Extension and Macintosh File Type
](https://www.w3.org/TR/rdf-sparql-XMLres/#mime)で定義されています。

HTTPリクエストのHTTPヘッダに`Accept: application/sparql-results+xml`を指定すると、レスポンスをJSON形式で取得できます。curlの例です。

```sh
curl 'http://ep.lodqa.org/qald-biomed/query' -H 'Content-Type: application/sparql-query' -H 'Accept: application/sparql-results+xml' -d 'SELECT (COUNT(?l) AS ?count) WHERE { ?x <http://www.w3.org/2000/01/rdf-schema#label> ?l . }'
```

例えば `http://bio2rdf.org/sparql` というURLのSPARQLエンドポイントはのレスポンスのデフォルト形式はHTML形式です。この場合、明示的にXML形式を指定する必要があるかもしれません。

# 参考資料

- [直感RDF!!　その１-RDFとは。 - Qiita](https://qiita.com/maoringo/items/4742b5cd01c9e698260d)
- [直感RDF!!　その2 -使いやすいRDFを作って，検索しよう。 - Qiita](https://qiita.com/maoringo/items/0d48a3d967a35581cc24)
- [直感RDF!!　その3 -外部のデータと繋げて使える検索をしよう。 - Qiita](https://qiita.com/maoringo/items/f9a5b33b36beaa6b427b)