[IM]追加の検索条件のマッチング

INTER-Mediatorでのページ状態の記憶の仕組みについて、幾つかの変更を行っています。変更前に動作の基本として、ローカルコンテキストは、ローカルストレージ(使えない時はクッキー)に保存をするようにしています。ローカルコンテキストには、ページファイル内にあるdata-im属性が「_@fieldName」のデータの置き場所を確保します。加えて、INTERMediator.addConditionメソッドでクエリーに追加する検索条件も、INTERMediatorオブジェクトのプロパティに見えるものの、実際にはそれをローカルストレージに保存しています。その結果、ページを開いた時にはローカルストレージは復元されるので、追加する検索条件も一度与えると明示的に消さない限りは、そのページに戻ったときに復元され、つまりは検索条件が常に保持されるように見える仕組みが組み込まれています。

ここで、ローカルストレージのキーに、URLを含めていましたが、URLのうちのクエリー部分は排除すべきかと考えました。例えば、ページファイルを開くときに渡すパラメータを与えて「page.html?id=101」などといったURLがある場合、ローカルストレージのキーに「page.html?id=101」までを含めるのか、「page.html」だけを含めるのか、どっちがいいのか(正しいのか?)を考えましたが、後者ではないかと思ったのです。クエリー部分があると、クエリーの内容によって以前の検索条件が消えるというのは、動作として正しくないと考えました。

ところがこうすると、こんな問題が発生します。例えば、page.htmlはクエリー部の情報を検索条件として付与するとします。「page.html?id=101」により、id=101という検索条件が加わり、ローカルコンテキストに保持されて残ります。そして、「page.html?id=55」をアクセスしたとき、どちらもpage.htmlなので、id=101という検索条件が残ったまま、id=55が付与されます。つまり、id=101 AND id=55というように、追加の検索条件が設定されてしまいます。idが主キーなら、検索されるレコードは0になってしまいます。これは意図しない動作です。

この問題には正解はないと思いますが、ロカールストレージのキーにクエリー部を含むURLを入れてしまうと、クエリー部によって、保持されるかどうかの挙動が変わってしまうことになります。一方、クエリー部を含めないでキーにすると、検索条件が追加されてしまって、意図しない動作になります。

そこで考えたのが、追加する検索条件については、マッチングをするということです。ローカルトレージのキーには、クエリー部は含めません。しかし、すでにid=101を検索条件として記憶している時に、addConditionメソッドで条件を追加するときには、条件のオブジェクトのfieldおよびoperatorプロパティが同一のもがあれば、その値のみを置き換えるようにします。id=55を追加すると、id=101とid=55のfieldおよびoperatorは同一なので、id=55の方だけが残るようにします。なお、こうした動作ではなく、従来と同様に単純に追加したい場合は、addConditionの3つ目の引数にtrueを指定することで、マッチングをしないで追加をするようにしました。ここまでをすでに実装しました。

そして、INTERMediator.clearConditionは、ローカルコンテキストの追加検索条件をクリアしていますが、同時に、ローカルストレージもクリアするのが正しいのではないかと考えています。すぐに実装は可能です。

[IM] OAuth2 (Open ID)による認証の実装

現在の認証方式は、[IM]INTER-Mediatorの認証フレームワーク(2)に記載の通りですが、これのベースにOAuthを動作させたいと考えています。ということで、とりあえず、Googleをベースに考えてみました。ブログなどでよく見られる「Google対応」は、2.1、4.1、4.2の部分です。Googleで認証をした結果を取り出すという部分です。しかしながら、そこから後が長いですね。その認証で得られた情報を元に、アプリケーション自体の認証もしなければなりません。OpenIDのtoken_idをそのままクッキーに入れるといった実装も見られますが、実際の認証はすでに動作しているユーザー名とクレデンシャルを使った方法を利用します。Open IDのtoken_idがあれば、その署名の検証を行うことで、内容を信頼するようにしようかと考えています。一番、問題なのは8.4で、クレデンシャルの期限切れがあったときに、クレデンシャルを問い合わせるか、あるいは再生成して再設定するかを行うことになると思います。その時、token_idの中身を見て、ユーザー名を特定することで、確実に動作してくれないかと考えています。ご意見よろしくお願いします。

シーケンス図0

[IM] バリデーションをどう実装するのか?

バリデーションに関しての実装は、バリデーションの実装を再考するや、Facebookのメッセージにも書きましたが、なんとなく実装が見えてきた感じがするので、一度ドキュメントにしてみたいと思います。

まず、UIからDBまで線を引いて、チェックポイントがどれだけあるかというと、これだけあります。もちろん、もっと細かくできますが、要するに出入りの部分となるとこうなります。

  1. テキストフィールドなどのデータがあるUIコンポーネント
  2. 送信ボタン等、データはないけど、データ送信を指示するUI
  3. AJAXの出口(Adapter_DBServer.js)
  4. サーバー側の受け入れ(DB_Proxy.php)
  5. データベースエンジンのスキーマに定義する制約

現状では、定義ファイルのコンテキスト定義に含めるvalidationキーによる連想配列の配列を設定することで、原則として、1そしてPost Onlyモードでの2のバリデーションが機能します。

ここでまず、3は不要と考えます。言い換えれば、4があるなら3はいらないということになります。5はフレームワーク外のものです。その有無とフレームワークの動作をどの程度連携させるのかは難しいですが、ここでポイントは「5はなくてもアプリケーションとして要求を満たせる」使用にしておくということです。

ちなみに、2は、Post Onlyモードと、IM_Entry関数のオプション変数でtransactionキーをnoneにして「保存」ボタンを表示した時になります。ここで、1つ考えられることは、1と2は同時に満たすようにはすることはまずないということです。もし、やりたい場合、「保存」ボタンでは現状APIはありませんが、Post Onlyモードはデータベースアクセス前に呼び出すメソッドを実装して、そこに機能を組み込むことができます。ということは、1と2は排他的であると言えるのではないでしょうか。

そうなると、(1 or 2) (4) [5] がチェックポイントになります。[5]については、データベースのエラーの扱いに含まれることになりますので、(1 or 2) (4) のチェックポイントが残ります。

そういうわけで、次のような使用を考えてみました。

A. validationキーの設定により(1)の状況を必ず満たす

validationキーの設定のあるフィールドとバインドした編集可能なUIコンポーネントは、フォーカスが外れた時に必ずバリデーションを行います。編集中のページはもちろん、Post Onlyモードでもそうします。また、初期値が違っている場合は、そこを変更しようとしてフィールドにフォーカスしたあと、そのまま別のフィールドに移動する時にチェックします。つまり、初期値はチェックしないのに等しいでしょう。

B. post-validationキーにより、(1)をやめて(2)にする

コンテキストに、新規に用意したpost-validationキーの値がtrueなら、(1)の状況ではチェックはせず、(2)の状況のみ、validataionキーで定義した内容に基づきチェックを行います。validation/notifyの指定がないフィールドが1以上ある場合、それらのメッセージをまとめて1つのダイアログボックスで表示します。post-validationキーの値が文字列の場合、ダイアログボックスで表示する。つまり、すべてのエラーメッセージをノードに表示したとしても、通知のためのダイアログボックスを出す手法を用意しておきます。

C. server-validationキーにより、サーバー側のバリデーションを有効化する

コンテキストに、新規に用意したserver-validationキーの値がtrueなら、サーバー側でvalidataionキーで定義した内容に基づきチェックを行います。notifyについては無視して、エラーをクライアントに返します。このキーの指定により、クライアントとサーバーの両方でチェックすることになる。ただ、こちらはエラー取得後の動作が悩ましいですね。

D. 外部キーフィールドの参照整合性のチェックは、ruleに特殊な関数あるいは記述で指定

外部キーフィールドにテキストフィールドを仮に利用したとしたら、こういう設定が欲しいかもしれません。しかし、よく考えると、そういう場合、ポップアップメニューにすれば解決するとおもいます。SELECT文のIN演算子の右側が欲しいわけですから、結果的にデータを取り出すコンテキストとフィールド名の指定が必要です。ただし、データベース接続が増えることや、レコードの増減を反映させることなどを考えれば、この仕様は優先度低いかなと思っています。

E. 複数のフィールドにまたがる検証はvalidationキーの指定を工夫する

例えば、{field: “f1, f2”, rule: “value1 != ” and value2 != ” “, message:”?”} ように、validationキーの連想配列の指定を複数に拡張します。

キータイプごとの検証や、検索条件の適用はしないことにします。A.はほぼ実装できていますが、B、C、D、Eはまだです。この中で、優先順位的にはB-C-E-Dというところでしょうか。

さて、これらの使用でいかがでしょうか?

[IM] SQLの集計処理をサポート

INTER-Mediatorは宣言的な設定やあるいは動作上の状況から自動的にSQLステートメントを生成します。通常のリレーション取得はそれでもいいのですが、集計処理などでは、SQLのチューニングが必要になります。そこで、コンテキストにaggregation-select、aggregation-from、aggregation-group-byという3つのキーをサポートするようにしました。これらのキーがあると、viewキーはSQL生成では無視されます。また、読み込み処理のみをサポートするので、tableキー、keyキーは実質的に使われません。aggregation-select、aggregation-fromは両方とも指定する必要があります。これらのキーを設定すると、コンテキストからの読み込みに次のようなSQLを生成します。

SELECT [aggregation-selectの値]
FROM [aggregation-fromの値]
WHERE [query, relation, その他による検索条件]
ORDER BY [sort, その他によるソート条件]
GROUP BY [aggregation-group-byの値]
LIMIT [recordsの値]
START [オフセット値]

つまり、SELECT、FROM、GROUP BY句については、新たに導入したキーの値を利用します。aggregation-selectにSUM()などの集計関数による記述が使えるので、キーの名前にaggregationを含めています。もちろん、自動生成できないうなSQLの生成は集計だけに限りません。WHERE、ORDER BY、LIMIT、STARTはすでに組み込まれている様々な指定方法が反映されます。WHRE句は、コンテキスト定義のqueryだけでなく、relationキーによる関連レコードの検索、JavaScript上での追加条件の設定、ローカルコンテキストを利用した追加設定を利用できます、これらはすべてが実行されるSQLステートメントに反映されます。ORDER BY句も同様、コンテキストのsortだけでなく、JavaScriptやローカルコンテキストの指定も加わります。

なお、STARTについては、引き渡しは実装しましたが、現状では0でのみ利用してください。つまり、ページネーションは利用できないということです。パフォーマンスを考慮して、レコード数のカウントは、SQLの結果の数と同じにしてあるので、結果的に1ページ分しか出てこないでしょう。これは、後々改良をすることとします。

この機能によるパフォーマンス向上の効果を説明しましょう。例えば、大量の売り上げデータがあって、月ごとに集計したいとします。集計する方法は、SQLだけでなく、計算プロパティを使う方法ありますが、大量なので、処理を効率的にしたいため、データベース側で集計したいとします。aggregation-*キーがない場合には、月ごとの売り上げ集計結果が1レコードとなるようなビューを作成しておき、検索条件(例えば、年と月を指定)をビューに適用することになります。しかし、そのような動作だと、一旦ビューを構築するために全部のデータの集計を行うこともあり、一部のデータだけを使うという動作にならず、十分なパフォーマンスが得られません。しかし、aggregation-selectに「SUM(price)」のような記述が含まれていれば、WHERE句で対象月に絞り込んでクエリーを実施した上で集計されるので、全部のデータを取り出して処理をするということはなく、より最適化されたSQLが発行されます。

FROMを独立して指定できるようにしているので、ここに、「テーブル名 JOIN テーブル名 ON 条件」という記述によるテーブル結合もできます。aggregation-*キーはそのまま指定されるようになっていて、とりあえず、現状ではフィールド名のクォートなどはしていません。セキュリティ的に問題になる可能性もありますが、クライアントのユーザーによって改変できない内容なので、構築時に注意をしておけば基本的には問題ないでしょう。

サンプルは、Samples/Sample_Extensible/aggregation3.htmlです。サンプルの選択ページでは、「Aggregation Query」という列を作り、そこにサンプルページのリンクを作ってあります。