jQueryではじめるAjax

第4回 検索キーワードを提案するSuggest機能の実装

この記事を読むのに必要な時間:およそ 6 分

Suggestの実装(2)

Suggestの実行

Suggest処理を実行し,Suggestを表示しますリスト7⁠。

リスト7 Suggestの実行

// --- suggest実行 ---
suggest: function(force) {
    this.stop_suggest();

    if (force) this.preinput = null;
    this.tid = setTimeout(function(){yt.do_suggest()}, Youtube.SUGGEST_TIME);
},

まず,実行中のSuggest処理を停止します。

this.stop_suggest();

引数forceがtrueであれば,前回のSuggest検索文字列にnullを代入しSuggestが必ず実行されるようにします。 preinputは,Youtubeコンストラクタ関数のprototype プロパティに定義されています。

if (force) this.preinput = null;

Suggest処理を実行します。 ただ,キーが押されるたびにYoutubeへリクエストを送ることは効率的ではないので,500ミリ秒後に実行されるようにし, 検索テキストボックスに入力された文字列は,yt.do_suggest()メソッドの内部から取得します。 そして,yt.do_suggest()メソッドでは,検索テキストボックスに入力された文字列に変更があった場合のみ検索処理を実行します。

this.tid = setTimeout(function(){yt.do_suggest()}, Youtube.SUGGEST_TIME);

Youtube.SUGGEST_TIMEは次のように定義されています。

Youtube.SUGGEST_TIME = 500;  // Suggestまでの時間(ミリ秒)

Suggestの停止

clearTimeoutを実行して,キューに登録されているSuggest処理があればそれをクリアしますリスト8⁠。

リスト8 Suggestの停止

// --- suggest停止 ---
stop_suggest: function() {
    clearTimeout(this.tid);
},

Suggest処理

検索テキストボックスに入力された文字列でビデオを検索し,ビデオに設定されているキーワードからSuggestを生成しますリスト9⁠。

リスト9 Suggest処理

// --- suggest処理 ---
do_suggest: function() {
    // Suggestが表示されていたら非表示にする。
    $("#suggest").hide();

    // 検索キーワードの処理
    var str = $("#keyword").val();
    if (str == null || str.length == 0) return;
    if (this.preinput == str) return;
    this.preinput = str;

    // ajax通信定義
    $.ajax({
        dataType: "jsonp",
        data: {
            "vq": str,
            "max-results": 10,  // 10件分を検索
            "alt":"json-in-script"
        },
        cache: true,
        url: "http://gdata.youtube.com/feeds/api/videos",
        success: function (data) {
            // 検索キーワードにマッチするデータがない
            if (data.feed.entry == null) return;

            var suggests = [];
            $("#suggest").empty();  // Suggestをクリア
            $.each(data.feed.entry, function(i, item){
                // ビデオに設定されているキーワードがない
                if (item.media$group.media$keywords == null) return true;
                // キーワードを配列に変換
                var keywords = item.media$group.media$keywords.$t.split(", ");
                if (keywords.length == 0) return true;
                // 各キーワードを判別
                $.each(keywords, function(n, keyword){
                    if ((keyword != str) &&                      // 検索キーワードと違う
                        (keyword.indexOf(str) == 0) &&           // 検索キーワードで始まる
                        ($.inArray(keyword, suggests) == -1)) {  // 既にSuggestに追加されていない
                        suggests.push(keyword);
                        $("#suggest").append($("<option/>").text(keyword));
                    }
                });
            });

            // Suggestが0件
            if (suggests.length == 0) return;
            // Suggestを表示
            $("#suggest")
                .show()
                [0].selectedIndex = -1;
        }
    });
}
前処理

まず,Suggestが表示されていたら非表示にします。

// Suggestが表示されていたら非表示にする。
$("#suggest").hide();

次に,検索テキストボックスに入力されている文字列を取得し,処理を行います。

var str = $("#keyword").val();

もし,検索文字列が空もしくは前回と同じである場合には,何も処理を行いません。

if (str == null || str.length == 0) return;
if (this.preinput == str) return;

最後に次回の検索文字列と比較するために,検索文字列を保存しておきます。

this.preinput = str;
ajax通信定義

基本的な構造は前回までのビデオ検索のものと変わりません。 よって,successコールバック関数のSuggest生成部分を抜粋して説明します。

各ビデオのエントリをイテレーションし,ビデオに設定されているキーワードを処理します。

$.each(data.feed.entry, function(i, item){

    ・・・

});

ビデオに設定されているキーワードがない場合は,media$keywordsプロパティ自体が存在しません。 よって次のようにしてキーワードが設定されているかどうかを判別し,キーワードがない場合には次のビデオのエントリを処理します。

// ビデオに設定されているキーワードがない
if (item.media$group.media$keywords == null) return true;

$.eachのコールバック関数がtrueを返すと,次のイテレーションを実行するのでしたね ⁠falseを返した場合は,$.eachの処理を終了します⁠⁠。

キーワードはカンマ+スペース「, 」で区切られて格納されています。扱いやすいように配列に変換します。

// キーワードを配列に変換
var keywords = item.media$group.media$keywords.$t.split(", ");
if (keywords.length == 0) return true;

キーワードの配列からSuggest文字列を抜き出します。 今回,Suggestの文字列は,検索テキストボックスの文字列で始まるキーワードとしました。

$.each(keywords, function(n, keyword){
    if ((keyword != str) &&                      // 検索キーワードと違う
        (keyword.indexOf(str) == 0) &&           // 検索キーワードで始まる
        ($.inArray(keyword, suggests) == -1)) {  // 既にSuggestに追加されていない
        suggests.push(keyword);
        $("#suggest").append($("<option/>").text(keyword));
    }
});

$.inArray(value, array)は, 配列arrayから,valueを検索し,配列内でのインデックス番号を返します。 valueが配列に存在しない場合は,-1 を返します。 ここでは,Suggest文字列の重複を避けるため,Suggest文字列配列suggestsに存在するかどうかを判別しています。

そして,Suggest文字列からoption要素を生成し,Suggestのselect要素の子要素に追加します。

最後に,Suggest文字列が1件以上あれば,Suggestを表示します。

// Suggestが0件
if (suggests.length == 0) return;
// Suggestを表示
$("#suggest")
    .show()
    [0].selectedIndex = -1;

以上で全ての実装が終了しました。サンプルを実行して動作を確認してみてください。

まとめ

今回はSuggestを実装してみました。 第3回までの内容が理解できていれば,思ったより簡単だったと思います。

この実装では,Suggest文字列をビデオに設定されたキーワードから生成しているため,Suggestが表示されるまでに少し時間がかかります。 検索件数を少なくしたり,Suggest文字列の切り出し処理を最適化すればもう少し速くなると思います(YoutubeがSuggestAPIを公開してくれればよいのですが…⁠⁠。

次回は,ユーザインターフェースライブラリjQuery UIを取り上げたいと思います。

著者プロフィール

池田正一(いけだまさかず)

仕事ではもっぱらJavaを使い,たまにC/C++を書かされ,WebサービスをRubyで開発するプログラマ。ドラえもん好きでドラえもんSuperDatabaseの管理人。stacktrace.jpにて頭の中のStackを出力中。