2010/12/11

FlexBoxの検索を絞るためにQueryにキーを追加してみる

前回は 郵便番号検索の中で、町村名などを入力する際に jQueryプラグインのFlexBoxを使って動的に検索結果を表示させました。

配列などで静的に候補を指定する場合には、対応するautocomplete系のjQueryプラグインは複数あります。 都道府県レベルでは、これでも十分に対応できます。

しかし郵便番号データベースに登録された市区郡以下の町村レベルになると、9万5千件を越えるアイテムがあります。 前回からjQueryプラグインのFlexBoxを使うことで、page, sizeの指定による部分的な結果を要求するFlexBoxは対象数が大きな場合に効果的である事がわかりました。

とはいえ、他にキーとなる入力があれば、それを一緒に渡す事でさらに検索対象を狭めることが期待できます。 これはエンドユーザへのより適切な選択肢の提供を目的としたものです。

今回は完全一致が期待できる都道府県名に注目をして、ここに値が入力されていた場合には、その都道府県名をキーとして町村名の検索範囲を狭めることにしました。

これによって大量の候補を扱う事によるメモリの消費も抑える事ができ、全体的な印象はかなり改善されたと思います。

変更したFlexBoxの動きについて

FlexBoxが動的に候補を表示する場合に使うQUERY_STRINGのkeyは、"s","p","q","contentType"の4つだけでした。 今回はこのkeyに kを追加します。

"k"に対応する値には $(QueryKey).val()の結果を渡すようにしていて、この"QueryKey"自体はオプションとして指定された文字列です。

QueryKeyの値としては、"#input-pref"のようなセレクタを想定しています。

アクセスログに残っているリクエス行は次のようなものでした。

... "GET /postal/street.1.4.fcgi?q=&p=1&s=10&contentType=application%2Fjson%3B+charset%3Dutf-8&k= HTTP/1.1" 200 50 ...

この"k"に指定された値をCouchDBのViewのstartkeyとendkeyに渡すことで検索を行なっています。

変更したFlexBoxの使いどころ

Google Toolbarの検索ボックスのように、単独で入力文字を補完するような場合にはノーマルなFlexBoxが向いていると思います。

今回の変更は、同じ画面で別のフォームに入力された従業員番号などの情報も一緒にした検索を可能にします。 より適切な対象を候補として表示する事が可能になることを期待しています。

自分の目的には2つの値をキーとして渡す事も考えていて、汎用的な拡張は難しいですが、各種の目的に応じて検索候補を要求するqueryを変更するのは良いアイデアのように思えます。

とりあえずjQuery + FlexBoxを使って、いろいろな方法で入力補完を試してみようと思います。

修正した後のアプリケーションの動きについて

現在は 郵便番号検索のトップページから開発版に進むと、FlexBoxを使ったautocompleteを試す事ができます。

いま現在の動きは「都道府県名」を完全に入力した場合に、町村名が補完されるようになります。

サーバはメモリが256MBのプランで、最大512MBまで使えるとはいっても、いろいろキャッシュする事はできません。 とはいえCouchDBと補完用候補検索用のRubyスクリプトはよく動いてくれています。

CouchDBが常に使っている常駐メモリ(RSS)分は23MB、FastCGIのメモリは検索対象の大きな町村名を扱う部分で15MBほどです。 仮想メモリ全体でも100MBほどですから、DTIのVPSを使っている分には正解な感じです。

Alixで使う分にも問題はないのですが、大きなViewを一気に作る時にはCPUパワーが足りない印象です。それでも稼働確認としての動き自体にはやはり問題ありません。

修正したコードのdiff出力

diffの出力は下記のとおりですが、なんというか、小さい変更で良い結果を手に入れることができました。

$ diff -u jquery.flexbox.js.orig jquery.flexbox.js

flexbox 0.9.6に対するdiffの結果

--- jquery.flexbox.js.orig	2010-11-24 13:03:02.000000000 +0900
+++ jquery.flexbox.js	2010-12-11 11:57:56.000000000 +0900
@@ -1,3 +1,9 @@
+/*
+ * This file contains some modification written by Yasuhiro ABE <yasu@yasundial.org>
+ * Copyright (c) 2010 Yasuhiro ABE (http://www.yasundial.org)
+ * Original copyright is following;
+ */
+
 /*!
 * jQuery FlexBox $Version: 0.9.6 $
 *
@@ -53,6 +59,7 @@
         scrolling = false,
         pageSize = o.paging && o.paging.pageSize ? o.paging.pageSize : 0,
 		retrievingRemoteData = false,
+	queryKey = o.queryKey, // added by Yasuhiro ABE
         $div = $(div).css('position', 'relative').css('z-index', 0);
 
         // The hiddenField MUST be appended to the div before the input, or IE7 does not shift the dropdown below the input field (it overlaps)
@@ -252,7 +259,9 @@
                     showPaging(p, cached.t);
                 }
                 else {
-                    var params = { q: q, p: p, s: pageSize, contentType: 'application/json; charset=utf-8' };
+                    //var params = { q: q, p: p, s: pageSize, contentType: 'application/json; charset=utf-8' };
+                    var kQueryString = $(queryKey).val();
+		    var params = { q: q, p: p, s: pageSize, contentType: 'application/json; charset=utf-8', k: kQueryString };
                     var callback = function(data, overrideQuery) {
                         if (overrideQuery === true) q = overrideQuery; // must compare to boolean because by default, the string value "success" is passed when the jQuery $.getJSON method's callback is called
                         var totalResults = parseInt(data[o.totalProperty]);
@@ -848,7 +857,8 @@
             showSummary: true, // whether to show 'displaying 1-10 of 200 results' text
             summaryClass: 'summary', // class for 'displaying 1-10 of 200 results', prefix with containerClass
             summaryTemplate: 'Displaying {start}-{end} of {total} results' // can use {page} and {pages} as well
-        }
+        },
+	queryKey: '' // will be added to the k=$(QueryKey).val() to the outer query url
     };
 
     $.fn.setValue = function(val) {

まとめ

もともと町村名には最大で9万5千件の候補が表示されるようになっていましたが、これ自体はFlexBoxが範囲を区切って候補を要求するためパフォーマンス上の問題ではありませんでした。

しかしユーザには常に9万5千件の一部が表示される状態だったため、検索対象を絞るための情報を有効に活用するのが利便性を向上させるだろうと考えたのが今回のFlexBox改造の動機でした。

CouchDBをバックエンドに持っていて、FastCGIやCouchDBの再起動時にこそ表示が素早く行えないなどの問題がありますが、通常はほぼ問題のないレスポンスを返しているようです。

もちろん利用者が自分だけという理由もありますが、パフォーマンスの改善にはまだ余地があります。 VPSの256MBのプランですし、テストベンチにしてはよく動いていると思います。

JavaScriptについて

関数型言語として興味はありますが、ブラウザのDOMを操作する事にはあまり熱心になれないので、JavaScriptは得意ではありません。

そのためGoogleで多くのJavaScriptについての検索を行ない、いろいろ参考にさせて頂きました。

しかしいくつかの解説の中には"id"値が 文書全体で唯一の要素を指定するために使われるってことをどれぐらい意識しているのかと疑問に思う場面がありました。

JavaScriptを使うと入力をサポートすることができますが、強制する事は何一つできません。

ユーザからの入力は適切な範囲に収まっているかサーバ側で検証する必要があるのは、まず大切なことです。

0 件のコメント: