最速検索の勉強

○○最速検索ってのを作ってみたいです。とりあえず有名なawsearch.js - Amazon最速検索ライブラリの勉強開始。

うーん、JavaScriptXSLTですか。両方苦手なのでちょっと手間取りそう。

awsearch.js

うろ覚えのJavaScript知識で読み進めてみます。

* 利用例
var dev_token = "あなたのdeveloper token";
var xslt = "http://example.com/awsearch.xsl";
var aws = new AWSearch(dev_token, xslt, {
  mode: 'music-jp',
  onSuccess: function(result){
    alert(result.items[0].title + ' - ' + result.items[0].creator.join(','));
  }
});
aws.search('Beatles');
aws.search('Billy Joel');

AWSearchがクラスと。パラメータをつけてインスタンスを生成しawsに代入と。aws.searchで検索実行と。AWSearchのコンストラクタの第三引数は連想配列かな(文法忘れた、確かkey: valueのペアを並べて書けた)。modeは「和書」とかカテゴリを指定するっぽい。onSuccessで検索成功したときのコールバックを指定すると。コールバックの引数としてresultに連想配列が渡されてくると。

ふむふむ。

var AWSearch = function(dev_token, xslt, options){
  return new AWSearch.Search(dev_token, xslt, options);
};

AWSearch.results = {};
AWSearch.callbacks = {};

おや。AWSearchはクロージャですかね。さっきnew AWSearchとかしてたのはちょっと不思議ですが読み進めましょう。内部ではAWSearch.Searchをnewして返していますね。検索処理に共通の機能やデータをAWSearchに持たせて、個別の検索はAWSearch.Searchに持たせているっぽいです。とするとresultsは直前の検索結果でしょうか。callbacksはコールバックそのものっぽいですね。

次はユーティリティクラスっぽいですね。

AWSearch.Utils = {
  extend: function(destination, source) {
    for (property in source) {
      destination[property] = source[property];
    }
    return destination;
  },

  bind: function(method, object) {
    return function() {
      return method.apply(object, arguments);
    }
  }
};

extendはプロパティを上書きコピー。bindはメソッドをオブジェクトに追加しているようです(Rubyでいう特異メソッドかな)。

AWSearch.Searchの定義はこんな感じ。

AWSearch.Search = function(){ this.initialize.apply(this, arguments); };
AWSearch.Search.prototype = {
  ...
};

1行目はコンストラクタinitializeにパラメータを渡しているようです。JavaScriptだとこうなるんですね。2行目以降はAWSearch.Searchクラスのメソッド定義みたいです。

実際の定義内容。

  initialize: function(dev_token, xslt, options){
    if(!dev_token)
      throw('dev_token is required.');
    if(!xslt)
      throw("xslt's uri is required.");
    this.dev_token = dev_token;
    this.xslt = xslt;
    this.setOptions(options);
  },

コンストラクタの定義です。パラメータをローカル変数に代入しているようです。

  setOptions: function(options) {
    this.options = {
      asid: 'nomusinolife-22',
      searchType: 'KeywordSearch',
      mode: 'books-jp',
      type: 'lite',
      page: 1,
      locale: 'jp'
    };
    AWSearch.Utils.extend(this.options, options || {});
  },

オプションのデフォルト値を定義し、指定されたオプションで上書きしています。

  search: function(keyword){
    if(!keyword)
      return false;
    var script = document.createElement('script');
    script.charset = 'UTF-8';
    var name = '';
    with(this.options){
      script.src = 'http://xml-jp.amznxslt.com/onca/xml3?t=' + asid + '&dev-t=' + this.dev_token + '&' + searchType + '=' + encodeURI(keyword) + '&mode=' + mode + '&type=' + type + '&page=' + page + '&f=' + this.xslt + '&locale=' + locale;
      name = mode + '_' + keyword + '_' + page;
    }
    AWSearch.callbacks[name] = AWSearch.Utils.bind(function(result){
      this.count = result.count;
      if(this.options.onSuccess)
        AWSearch.Utils.bind(this.options.onSuccess, this)(result);
    }, this);

    if(typeof this.options.onCreate == 'function')
      this.options.onCreate();
    document.body.appendChild(script);
  }

scriptタグを作る。そのソースをアマゾン検索APIとする。パラメータは、オプションを適切につなげる。ここで、xslを使う。コールバックをセットする。onCreateが指定されていたらこのタイミングで呼び出す。scriptタグをdocument.bodyに追加する。

コールバックのところは難しいですね。

    AWSearch.callbacks[name] = AWSearch.Utils.bind(function(result){
      this.count = result.count;
      if(this.options.onSuccess)
        AWSearch.Utils.bind(this.options.onSuccess, this)(result);
    }, this);

bindを使って、現在のコンテキストを第1引数のクロージャに割り当てます。クロージャ内のthisも現在のコンテキストっぽいですね。onSuccessが定義されていたらこれも現在のコンテキストで呼び出すようです。