SPARQL Endpoint に AJAX でアクセスする方法

SPARQL Endpoint にブラウザ javascript から AJAX でアクセスする方法を紹介します。またこのポストは [SPARQL 1.1 Protocol](http://www.w3.org/TR/sparql11-protocol/) の紹介も兼ねています。

# 1. シナリオ

DBpedia Japanese の SPARQL Endpoint のトリプル数を表示する HTML ページを作りなさい、というお題が出ました。エンドポイントの URL とトリプル数をカウントするための SPARQL は以下のように与えられています。

```txt:endpoint
http://ja.dbpedia.org/sparql
```

```txt:sparql
select (count(*) as ?count) where {?s ?p ?o.}
```

なお DBpedia Japanese は [SPARQL 1.1 Protocol](http://www.w3.org/TR/sparql11-protocol/) に準拠しており、GET や POST のアクセスを受け付けることが可能です。

![image](https://qiita-image-store.s3.amazonaws.com/0/82494/0f796966-5a5e-cbea-451a-a5693e9ea4f3.png)

<http://www.w3.org/TR/sparql11-protocol/#query-operation> によれば QUERY オペレーションに対して以下の3種類の方法が定義されています。

* query via GET
* query via URL-encoded POST
* query via POST directly

実際にこの方法が使えるか試してみましょう。

# 2. Query via GET

```html:query-via-get
<script>
  var endpoint = "http://ja.dbpedia.org/sparql";
  var query = "select (count(*) as ?count) where {?s ?p ?o.}";

  var x = new XMLHttpRequest();
  x.onreadystatechange = function() {
    if (x.readyState == 4 && x.status == 200)
      document.body.appendChild(document.createTextNode(x.responseText));
  };
  x.open("GET", endpoint + "?query=" + encodeURIComponent(query), true);
  x.setRequestHeader("Accept", "application/sparql-results+json");
  x.send();
</script>
```

この方法はうまくいきます。クエリーが比較的短い文字列の場合にはオススメです。

`x.setRequestHeader("Accept", "application/sparql-results+json");` 
によって SPARQL Endpoint に対して JSON 形式のレスポンスを返すように指示していることに注意してください。Content-negotiation という方式です。SPARQL 1.1 としては以下のようなレスポンスフォーマットが定義されていますが、フォーマットをサポートするかどうかは Endpoint 次第です。

* [SPARQL 1.1 Query Results JSON Format](http://www.w3.org/TR/sparql11-results-json/)
* [SPARQL 1.1 Query Results CSV and TSV Formats](http://www.w3.org/TR/sparql11-results-csv-tsv/)
* [SPARQL Query Results XML Format (Second Edition)](http://www.w3.org/TR/rdf-sparql-XMLres/)

### (補足)

よく `?query=...&format=text/html&debug=on` のように query 以外のパラメータのついた SPARQL リクエストを見かけることがあると思いますが、 基本的には各エンドポイントが独自に拡張したものだと考えておいたほうがいいでしょう。
SPARQL 1.1 Protocol としては `query`  `default-graph-uri` `named-graph-uri` だけが定義されています。





# 3. Query via URL-encoded POST

```html:query-via-url-encoded-post
<script>
  var endpoint = "http://ja.dbpedia.org/sparql";
  var query = "select (count(*) as ?count) where {?s ?p ?o.}";

  var x = new XMLHttpRequest();
  x.onreadystatechange = function() {
    if (x.readyState == 4 && x.status == 200)
      document.body.appendChild(document.createTextNode(x.responseText));
  };
  x.open("POST", endpoint, true);
  x.setRequestHeader("Accept", "application/sparql-results+json");
  x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  x.send("query=" + encodeURIComponent(query));
</script>
```

この方法もうまくいきます。クエリーがとても長い場合にはこちらを選択するとうまくいくかもしれません。

GET の場合とだいたい一緒ですが
`x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");` 
をつけることを忘れないようにしましょう。

# 4. Query via POST directly

```html:query-via-post-directly
<script>
  var endpoint = "http://ja.dbpedia.org/sparql";
  var query = "select (count(*) as ?count) where {?s ?p ?o.}";

  var x = new XMLHttpRequest();
  x.onreadystatechange = function() {
    if (x.readyState == 4 && x.status == 200)
      document.body.appendChild(document.createTextNode(x.responseText));
  };
  x.open("POST", endpoint, true);
  x.setRequestHeader("Accept", "application/sparql-results+json");
  x.setRequestHeader("Content-Type", "application/sparql-query");
  x.send(query);
</script>
```

この方法はうまくいかないのでオススメしません（もしもうまくいく方法があれば教えてください）。かわりに Query via URL-encoded POST を使いましょう。

### (補足)

この方法を使った場合のログを観察すると **POST** リクエストが投げられる前に **OPTIONS** リクエストが発行されていることがわかります。

![image](https://qiita-image-store.s3.amazonaws.com/0/82494/daca9444-21f7-0927-f4a2-15b6f055b189.png)

これは [W3C | Cross-Origin Resource Sharing| 7.1.5 Cross-Origin Request with Preflight](http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0)として規定されている挙動であり異常なものではないとのことです。ただ、これをパスして SPARQL Endpoint にアクセスする根性がないのでここでは Query via URL-encoded POST をオススメしておきます。


# 5.まとめ

* SPARQL Endpoint に SPARQL 1.1 Protocol に従った方法で AJAX でアクセスする方法を紹介しました
* AJAX でコンテントネゴシエーションに対応したリクエストを作成する方法を紹介しました
* 短いクエリーなら GET、長いクエリーには POST を使いましょう
* Query via POST directly はブラウザから使うには不都合があるのでご注意を