Hatena::Groupkdri

KazusaAPI開発日誌 このページをアンテナに追加 RSSフィード

2008-01-27

RESTful な blast 検索サービスを作るとしたらどうするかを考えてみた

|  RESTful な blast 検索サービスを作るとしたらどうするかを考えてみた - KazusaAPI開発日誌 を含むブックマーク はてなブックマーク -  RESTful な blast 検索サービスを作るとしたらどうするかを考えてみた - KazusaAPI開発日誌  RESTful な blast 検索サービスを作るとしたらどうするかを考えてみた - KazusaAPI開発日誌 のブックマークコメント

バイオインフォマティクス業界的には解析ワークフローというか解析パイプライン構築の環境整備がここ数年の課題になっている。ウェブサービスをつかって公開サービスをつなげるというのが狙いで、それには SOAP を使ってやりとりしましょうという流れになっている。

その一方で、ここ最近注目されつつある RESTful なウェブサービスはどうするのかというのも課題になりつつある。このエントリでは、よくあるデータベース検索(blast 検索)のウェブサービスSOAPREST で構築するときの違いを考察してみたい。


blast 検索サービス

DNAアミノ酸配列データベース検索の blast 検索サービスには、検索対象のデータベースdb)と問い合わせ配列(query)、検索オプション(options)の3つの入力が必要である。通常は、サービス提供者の作成した db に対する検索としてサービスするので、dbユーザが選択することは無い。したがって、ユーザからの入力には query と options となる。

コマンドラインでの blast の実行は、blastall というプログラムを用いる。

$ blastall -p blastp -d db -i query

blastp という検索プログラムを選択し、db は事前に formatdb によって専用のインデクスを作成したデータベース名であり、query は fasta フォーマットファイル名である。


DDBJ における SOAP での blast 検索の実装事例

screenshot

WSDLhttp://xml.nig.ac.jp/wsdl/Blast.wsdl)と上記のドキュメントによると、6 個のメソッドが提供されている。そのうち検索を実行するもの(search*)が 4 個、検索対象データベース名を取得するもの(getSupportDatabase)が 1 個、のこりは検索結果をパースするもの(extractPosition)。

検索を実行するメソッドは、検索オプションの有無と同期/非同期(*Async)の組み合わせだけある。

検索オプションありの例。

searchParam( "blastp", 
             "SWISS", 
             "MSSRIARALALVVTLLHLTRLALSTCPAACHCPLEAPKCAPGVGLVRDGCGCCKVCAKQL", 
             "-b 5 -m 7" )

検索オプションなしの例。

searchSimple( "blastp", 
              "SWISS", 
              "MSSRIARALALVVTLLHLTRLALSTCPAACHCPLEAPKCAPGVGLVRDGCGCCKVCAKQL" )

どの検索メソッドでも、検索結果は通常のコマンドライン blastall の既定の検索結果となる。ただし、検索オプションで -m の値を設定した場合はその指定した形式になる。

ここまでのまとめ

DDBJ における REST での blast 検索の実装事例

screenshot

DDBJ では2007年の後半から REST でのサービスも提供している。

URI 設計

2. How to connect the REST services?

You need an environment of HTTP connection such as web browser or

programming language (Perl, Java, Ruby and so on).

The way to access REST services is as follows.

http://xml.nig.ac.jp/rest/Invoke?service=ServiceName&method=MethodName&param.....

Call the URL http://xml.nig.ac.jp/rest/Invoke? with service name, method name and parameters.

This system is recommended to access with POST method.

You can also use GET, however be careful that GET method has a limit of data transferring.

no title

ということなので、SOAP のメソッド名をそのまま移植したような実装になっている。Rails に馴染みのある人にとっては ActionWebServicescaffold に似ているということに気がつくと思う。

getSupportDatabaseList の REST 版の場合。

http://xml.nig.ac.jp/rest/Invoke?service=Blast2&method=getSupportDatabaseList

すべてのサービスhttp://xml.nig.ac.jp/rest/Invoke というエントリーポイントで受け付けられている。

searchParam の REST 版の場合。

http://xml.nig.ac.jp/rest/Invoke?service=Blast&method=searchParam&program=blastp&database=SWISS&query=MSSRIARALALVVTLLHLTRLALSTCPAACHCPLEAPKCAPGVGLVRDGCGCCKVCAKQL&param=-b%205%20-m%207

GET では URL の長さ制限があるので、query= が長い時は POST でも実行可能となっている。

返り値

XML ではなく、-m オプションで指定した通りのフォーマットテキストが返る。SOAP の返り値と同じものがテキストで返る。

エラーハンドリング

不正db 名や service 名を指定したときはエラーの文字列が返る。

No service of 'Blasaat' as a service and 'searchSimple' as a method.
ここまでのまとめ

RESTful blast 検索

単純なコマンドラインプログラムウェブサービスにする場合として、DDBJ での実装を下敷きに考えてみる。

仕様
  1. ユーザ認証は行わない。
  2. ユーザは、検索対象データベースを選択できる。
  3. ユーザは、任意の配列クエリにできる。
  4. ユーザは、いくつかの検索オプションを設定できる。
  5. 検索結果は、-m オプション依存する。
  6. 検索結果は、ほかのユーザには見せない。
  7. エラーHTTP レスポンスコードをつかう。
  8. ウェブブラウザでも扱える。

ユーザ認証をせずに、検索結果をほかのユーザに見せないようにするため、一時的な検索結果 token を発行して、その URIリダイレクトする方法を考えた。でもこれだと stateful なのか?

つぎに操作とリソースとメソッドを示す。

操作サービス出力
対象データベースリスト表示GET /databasesリスト表示
データベース検索フォーム表示GET /{database}検索フォーム付きのHTML
検索PUT /{database}検索結果URIへのリダイレクト
検索結果表示GET /{database}/{token}検索結果
検索結果削除DELETE /{database}/{token}

リソース接続はつぎのようにする。

  1. /databases -> /{databases}
  2. /{databases} -> /databases
  3. /{databases} -> /{databases}/{token}
  4. /{databases}/{token} -> /databases
  5. /{databases}/{token} -> /{databases}

つぎから、それぞれの操作とリソースの詳細を示す。


対象データベースリスト表示(GET /databases)

リソース /databases は、検索対象のデータベースリストである。リストにはデータベース名とデータベースリソースへのリンクがある。

出力表現は xhtmlul/li を用いる。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <title>Databases</title>
</head>
<body>
<ol id="path">
  <li><a href="/">Databases</a></li>
</ol>
<h1><a href="/">Example RESTful blast service</a></h1>
<ul id="databases">
  <li><a href="/SWISS">SwissProt</a></li>
  <li><a href="/TrEMBL">TrEMBL</a></li>
</ul>
</body>
</html>

リストの中身を取り出すには XPathid 名を指定して取り出す。Ruby では REXML をつかってつぎのようにする。ここでは、http://example.comサービスしていると仮定する。

require 'rexml/document'
require 'open-uri'
url = "http://example.com/databases"
html = REXML::Document.new(open(url).read)
xpath = '//ul[@id="databases"/li/a'
html.get_elements(xpath).each do |db|
  db.attributes["href"] #=> "/SWISS"
  db.text #=> "SwissProt"
end

//ul[@id="databases"]/li/a という XPathアクセス出来ない場合は、 REXML::XPath は xhtml では @class などが働かない - KazusaAPI開発日誌 - KDRIグループ を参照されたし。既知のバグの場合があります。

サービスルート URI は //h1/a XPathアクセスできる。


データベース検索フォーム表示(GET /{database})

リソース /{database} は、検索対象のデータベースである。データベースリソースは、検索フォームを提供する。

出力表現は xhtml でフォームを構築します。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <title>SwissProt</title>
</head>
<body>
<ol id="path">
  <li><a href="/">Databases</a></li>
  <li><a href="/SwissProt">SwissProt</a></li>
</ol>
<h1><a href="/SwissProt">SwissProt - Example RESTful blast service</a></h1>
<form method="post" id="form-blast">
  <label>Query:<textarea name="query" id="form-blast-query"></textarea></label>
  <label>Options:<input type="text" name="options" id="form-blast-options" /></label>
  <label>Submit:<input type="submit" /></label>
</form>
</body>
</html>

検索(PUT /{database})もしくは(POST /{database})

リソース /{database} への PUT もしくは POST は、検索の実行となる。

上記のフォームで構築されたリクエストを送る。

POST /SwissProt HTTP/1.1
Host: example.com

options=&query=MSSRIARALALVVTLLHLTRLALSTCPAACHCPLEAPKCAPGVGLVRDGCGCCKVCAKQL

レスポンスは、検索結果URI(/{database}/{token})へのリダイレクト(301 Moved Parmanently)とする。

301 Moved Parmanently
Location: http://example.com/SwissProt/1689d6d9f62e378493dbd9776fabe2ac

ここでは一時的な token を Digest::MD5.new(str).hexdigest で作成してみた。strリクエスト文字列から生成する。


検索結果表示(GET /{database}/{token})

リソース /{database}/{taken} は、検索結果である。データベースリソースクエリを送って検索するとリダイレクトされる。

表現は、blastall 標準出力のそのままで。ただし、ヒットがなかったときは、テキストではなくて HTTP レスポンスで 404 Not found を返したい。不正な token の時は、400 Bad Request とする。blastal は XML 形式の出力(-m 8)でもヒットが無い時には XML ではなく単なるテキストを吐き出す邪悪な実装なので、それはラップしておきたい。

検索結果の存在時間を伸ばすと、検索のキャッシュとしてあつかえる。ただし、クライアントが token を生成する必要がある。


検索結果削除(DELETE /{database}/{token})

リソース /{database}/{taken} は、検索結果である。

明示的に消したい時に利用する。


ここまでのまとめ
  • 実装はまだない。
  • 問い合わせ配列を Accession で指定できるときはこれとは違う URI 設計になる。(これもよくある設計要求)

まとめ

  1. RESTful blast 検索サービスを考えてみた。実装はまだない。
  2. クライアント人間以外のときは、エラーの表現が形式的なほう(HTTP status コードなど)がよい。
  3. 慣れかもしれないが、コマンドラインアプリケーションRPC 的な SOAP の方が直接的に設計できそう。
  4. REST はなんらかの Best Practice やデザインパターン的に名付けられるものが普及するまえに不完全なまま REST を名乗るものがでてきて困りそう。
  5. サービスの振る舞いについての規格ができて、それを認証する仕組みが無いと相互接続とか難しいと思う。
  6. 接続性のための Microformats 定義に可能性があるとおもった。
トラックバック - http://kdri.g.hatena.ne.jp/nakao_mitsuteru/20080127