Category Archives: データベース

FileMaker Cloudから今後のFileMakerベースのWeb開発を考える

日本市場のユーザーはまだ使えないものの、FileMaker ServerをAmazon EC2上のLinuxで稼働させるFileMaker Cloud 1.15がリリースされ、多数のドキュメントが公開されました。確定情報や、将来構想も含めてここ3、4年の動向を検討できる材料が揃ってきています。

FileMaker 15が最新の現在において、FileMakerデータベースをそのままブラウザから利用できるWebDirectがFileMaker社一押しのWebソリューションであることは言うまでもありません。HTMLなどのコーディング不要であり、FileMaker Pro/Advancedで開発したデータベースをWebでそのまま利用できます。もちろん、一部に要注意点はありますが、そのあたりはドキュメントが完備されている上にあちらこちらのブログやBBS、そしてFileMaker Communityを検索すれば、何かしら解決できる状態になっています。いわゆる「Web特有の開発作業」をしなくてもWeb化できる点では非常にいいソリューションですが、一方、接続ライセンスが必要になることもあって、不特定多数を対象とするWebサイトの場合、ライセンスの範囲内に留めるための工夫が必要になります。技術的に算出は可能なものの、予測が当たるか当たらないかと言う要素は排除できず、一定の確率でアクセスできない場合が出てくることになります。自社向けの業務系システムでは問題ないものの、Webの世界は色々なサイトの利用のされ方があり、そうした要求を満たすにはWebDirectにだけ頼れないという実情があります。WebDirectはFileMaker 13より正式リリースされいくつかのバージョンを経て完成度は上がってきており、近々は細かなアップデートや「より正確な動作」を目指す段階に入っていると言えるでしょう。

一方、カスタムWeb、つまりXML共有やPHP共有を利用したWebシステム構築は、古い仕組みのままでした。特にXML共有はFileMaker Ver.5.5時代のものから本質的には変わっていません。一時期のインスタントWebはデータベースの再現性の低さもあり、プログラミング言語による開発が必要なカスタムWebと用途を分かつような感じでした。もっとも、インスタントWebでできないことが多かったので、カスタムWebに頼らないといけない状況でした。しかし、FM13よりWebDirectが登場しました。カスタムWebは無くなってしまうのではないかという予測はその前後からあったものの、FM15現在、若干の機能アップをしながら本質的には同様な仕組みがずっと搭載されています。カスタムWebはライセンスに縛られないで利用できる場合が一般的なので(ボリュームライセンスは同一組織に縛られる)、不特定多数が参照するサイトにおいても、ライセンス不足で参照できないということを配慮しなくてもよく、マシンパワー的な問題を考慮するだけでWebサイトを構築できました。

ところが、FileMaker CloudはカスタムWebを非搭載となっています。今年のDevConでのセッションの1つSneak Peak: Overview of the FileMaker Cloudでは、Cloudの構成についてすでに公開されており、こちらの内容を見る限りはカスタムWebはCloudには搭載されません。しかしながら、「FileMaker Data API」というRESTベースの機能がCloudに搭載される予定のようです。今回の最初のリリースにはこのAPIは含まれていないことから、順次あるいは次のメジャーアップデートではその方向で開発をしているということになります。そして、興味深いのは、カスタムWebはいつまで使えるのかということです。ここからは完全に予測ですが、FileMaker 16でFileMaker Data APIがCloud及びWindows/Mac対応のFileMaker Serverにも搭載され、FileMaker 18か19くらいまでカスタムWebが使えるというくらいになるのではないでしょうか。両方が並立する期間が数年はあると予測します。根拠は、FileMaker社がのDeprecated Features(使用できなくなった機能)として、ある機能がなくなる場合にはいくつかのバージョンをまたいでアナウンスをしていることがあります。カスタムWebがなくなるというアナウンスはないので、次のFM16には搭載されると予測できます。

しかし、何れにしても、3年を超える範囲を考えれば、そろそろカスタムWebの終焉を視野に入れる時がとうとうやってきたのかもしれません。もちろん、FileMaker Data APIが次のターゲットではありますが、システム単位ではもっとドラスティックにFileMakerを使わないという選択肢を考えることもあり得るでしょう。

もう1つ、前述のDevConのセッションの内容では、LDAP/Active DirectoryからOAuthベースへの移行を行うとしています。これをWindows/Macで稼働するFileMaker Serverでもその方針で進めるかどうかは微妙かと思います。これらディレクトリサービスによる認証は、WindowsやMacではOSに搭載されているから組み込まれた機能であって、Linuxでも同様な実装はできたはずです。しかしながら、FileMaker Cloudは独自に用意したインスタンスでありそこを見直して、いっそのことWebやその他、様々な状況での運用が可能なOAuthにするということでしょう。これは、うまくすると、GOで一度認証すると、同じサーバーで運用する幾つかのデータベースへのアクセス時には、本来の意味のシングルサインオンが機能して、ユーザー名やパスワードの入力が不要になるかもしれません。Macの中で実現していたことが、GO/Windows/Mac/DirectWebとシームレスに認証結果を共有できるようになるのだとしたら、素晴らしいでしょう。したがって、OAuthをFileMaker社の各アプリケーションが背後で実装していて、ユーザーは気づかないうちにその恩恵を得ている…というシナリオが理想です。さて、実際にどうなるのかは運用してみないとわかりません。何れにしても、FileMaker Data API時代のWebアプリケーションは、OAuth対応を考える必要が出てくると思われます。

最後にINTER-Mediatorです。もちろん、カスタムWebベースでの稼働はFileMaker Serverのサポートが続く限り継続させますが、FileMaker Data APIへの対応は必定であることは言うまでもありません。現在はスペックも、テスト稼働もできないのでなんとも言えませんが、開発ができるようになった段階からなるべく早く開発を進める予定です。

Server.appとFileMaker Server 14を共存させる

FileMaker Serverのアップデータが途中で止まる件は、こちらのページにまとめました。FileMaker Serverは、自身でhttpdコマンドのプロセスを起動するため、アップデータを適用するためにプロセスを止めた状態でないと処理しないのはいいとしても、FileMaker Server以外のサービスがhttpdを起動していても、やはりアップデートは進まず、そこで単なるシェルスクリプトによる繰り返しでhttpdのプロセスがなくなるまで待ってしまい、フリーズのようになってしまうという状況です。

OS X 10.11にServer.app Ver.5を入れた状態で、一度Webサービスを起動し、その後停止した状態を見ることができたので、記録しておきます。FileMaker Serverのプロセスを完全に落としても、以下のような2種類のhttpdのプロセスが残っています。-fオプションで指定されている設定ファイルを見れば、だいたいどんな機能のものなのかはわかります。

$ ps -ef|grep httpd
0 235 1 0 4:14PM ?? 0:00.47 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
70 358 235 0 4:14PM ?? 0:00.02 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
70 359 235 0 4:14PM ?? 0:00.06 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
70 360 235 0 4:14PM ?? 0:00.11 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
70 361 235 0 4:14PM ?? 0:00.06 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
70 362 235 0 4:14PM ?? 0:00.02 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
0 472 1 0 4:14PM ?? 0:00.35 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/apache2/services/ACSServer.conf -E /var/log/apache2/services/ACSServer_error_log
70 476 472 0 4:14PM ?? 0:00.02 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/apache2/services/ACSServer.conf -E /var/log/apache2/services/ACSServer_error_log
70 20919 235 0 5:36PM ?? 0:00.01 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
1026 28116 27955 0 5:57PM ttys001 0:00.00 grep httpd

apache_serviceproxy.confは文字通りプロキシで、通常のWebサービスやWikiなどのサービスを受け付けてプロセスに流すような設定になっています。つまり、リバースプロキシでしょうか。

ACSServer.confの方は、いろいろ見ていると、/AccountsConfigService/api/とかいったパスが出てくるので、ACSはAccounts Config Serviceだと思われます。いろいろな設定を追いかけると、/Applications/Server.app/Contents/ServerRoot/usr/libexec/scsdのプロセスがデーモンとして起動しているようで、それに対するWebインタフェースをApacheを使って稼働させている模様です。man scsdはきちんとドキュメントが表示され、「Accounts Config Service Daemon」とタイトルにあります。細かなことはわかりませんが、Webアプリケーションとして動くサービスとなっています。

何れにしても、これらのサービスが稼働していれば、FileMaker Serverのプロセスは起動しません。httpdを利用するサービスを全てなくすには、次のようなコマンドを打ち込みました。

$ cd /Applications/Server.app/Contents/ServerRoot/System/Library/LaunchDaemons/
$ sudo launchctl unload -w com.apple.serviceproxy.plist
$ sudo launchctl unload -w com.apple.service.ACSServer.plist

Server.appはいろいろな機能がありますが、Webサービスはもちろん、事実上はWebアプリケーションであるカレンダーやWikiなどの機能を一切使わないのなら、上記のようにしてリバースプロキシとACSServerはなくてもおそらくは問題ないと思われます。Server.appを、AFP/SMBのファイルサーバーとして、そしてTime Machineのサービスとして、さらにOpen DirectoryのMasterとして使う限りでは、上記のサービスを落とした状態でも利用できています。

ただ、これらのサービスは単純に「落としても構わない」とは言い切れないですし、皆さん自身が判断してください。ともかく、Server.appとFileMaker Serverを同時に使う場合には、Server.appのWebあるいはApacheを使っている一切の機能を使わないでおくようにするしかないと思います。ちなみに、聞くところによると、FileMaker社のサポートに問い合わせると「Server.appはアンインストールしろ」と言われるらしいです。それもどうかと思います。事実上、同じ会社の製品なんですけどね。

[IM] Ver.5.3よりサポートのsourceキー

Ver.5.3より、コンテキスト定義(定義ファイルのIM_Entry関数の第一引数の配列の要素)に、sourceキーを入れるようになっています。データベース上のエンティティ名に関連するキーとしては、name、view、tableがありますが、それにsourceが加わります。データベースから読み出しを行うときのFROM句に指定されるビューやテーブルの名前は、viewキーがあればその値、なければnameキーの値が使われます。CREATE/UPDATE/DELETコマンドの対象は、tableキーの値で、それがなければnameキーが使わます。例えば、nameキーにテーブル名があれば、そのテーブル名を指定しての読み書きはviewとtableキーの指定はしなくてもできます。しかしながら、表示は何かのビューを使い、更新はそのビューではない特定のテーブルを指定したいような場合に対処できるように、viewとtableキーを用意しました。

しかし、これだけでは問題があります。あるテーブルのあるレコードのあるフィールドが、ページ上の複数のリンクノードに展開された場合、それが例えば両方ともテキストフィールドであれば、一方の値を変えると、フィールド更新のためのサーバー通信が行われるとともに、クライアントサイドで同一フィールドの別リンクノードの値を更新できるようになっています。その時、何を手掛かりにして反映させるコンテキストを探しているかといえば、「viewないしはnameキーの値が同じだが異なるコンテキスト」です。例えば、住所録的なpeopleテーブルがあり、同一ページに全レコードの一覧と、その中で男性の一覧の両方があったとします。前者のコンテキストは、name=allmembers, view=people、後者のコンテキストはname=malemembers, view=peopleとして適切なqueryキーの条件を与えればいいでしょう。こうすれば、同一テーブルから異なるコンテキストを生成して、内容が異なる一覧を1ページ内で生成できます。

この時、男性の一覧にあるレコードは、必ず全員のレコードの一覧にもあります。どちらもviewキーがpeopleなので、男性のレコードの1つのフィールドを変更すると、対応する全員の一覧にあるレコードのフィールド値も更新されます。以下、この動作は「連動」と呼びます。同一のviewキーの値を持つコンテキスト同士なので、INTER-Mediatorにとっての手がかりがあります。

しかしながら、SQLのレベルで、peopleをもとにしたビューeveryoneとsomeoneがあったとします。それらで表を作るとしたら、nameやviewを使うにしても、everyoneとsomeoneがそれぞれのコンテキストに登場はしますが、コンテキストの情報から各々が同一のpeopleテーブルから導出されていることはわかりません。そうなると、同一のレコードがそれぞれの一覧に見えていて、一方の値を変更したとしても、その変更結果を伝達する手がかりがなく、連動はできません。

このような時には、一方のコンテキストを、name=everyone, source=people、もう一方はname=someone, source=peopleと定義します。ページファイル側は、everyoneあるいはsomeoneをdata-im属性に利用する点は変わりありません。このsourceの設定により、2つのコンテキスト定義から得られるコンテキストは、同一のテーブルから来ているものということがINTER-Mediatorに伝わるので、同一フィールド値がユーザーインタフェース上で連動するようになります。

[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」という列を作り、そこにサンプルページのリンクを作ってあります。

[IM]バリデーションの実装を再考する

INTER-Mediatorではかなり以前にバリデーションの実装はしたものの、完全ではない状態だったのですが、このところ、手を入れるに従って、いろいろ不具合…というか、「考慮が薄い」ポイントが目立ち始めましたので、改めて、議論を進めたいと考えています。

まず、バリデーションは、「入力チェック」とも言われます。いちばん、根本的なことは何かというと、「正しくない値をデータベースに保持しないようにしたい」という要求があると考えています。「正しくない」の基準は、アプリケーションによって変わりますが、「NULLである」ということかもしれませんし、「正しいメールアドレスのフォーマットではない」ということかもしれません。その場合に、データベースに記録しないようにするということがあります。データベース絡みとなると、いろいろ複雑な問題が出てきますので、これを後回しにして、まずは、アプリケーション利用者レベルからの見方を考えてみます。

開発者や管理者がバリデーションを必要とし、実装しますが、Webアプリケーションフレームワークは、バリデーションに関連したユーザーインタフェースを構築し、ユーザーを惑わせない仕組みの提供が望まれます。そもそも、アプリケーションで、どようにバリデーションが絡み合うのか、5W1H的にまず考えます。

  • When:正しくない値が入力されたとき
  • What:開発者や管理者が望ましくないと考えるデータを検知する仕組み
  • Where:入力可能なコンポーネント
  • Who:ユーザーが生成すると考える
  • Why:単純なミス、さまざまな誤認、テストあるいはインスペクション
  • How:キータイプ、あるいはコピー&ペーストなどのユーザの操作

以上の分析からは、バリデーションの検知と通知が大きな目的であることが出てきます。しかし、一部に例外があって、フィールドの初期値がバリデーション違反という場合もあります。そのとき、上記のWhoは「システム」ということになってしまいます。この初期値が違反している問題は、厳密にはデータベースの定義が正しくないで終わってしまいますが、非常に複雑な事情が絡むので、後ほど議論します。

バリデーションの検知は、INTER-Mediatorではすでに実装しています。onchangeイベントが発生したときに、フィールド単位でのチェックを行います。フィールドをまたがった判定用にも、コールバックされるメソッドの定義があります。最近になって、初期値が違反しているときでもバリデーションが働くように、onblurイベントでも判定をするようにしました。これら、さまざまなニーズはあると思われますが、タイミングと仕様が確定していれば、対処できる範囲かと思います。

一方、バリデーション違反の通知は、さまざまなバリエーションが考えられます。そういうニーズも状況も多様な状況では、プログラムを組んで対処というのはもちろん柔軟な対応ができていい部分ですが、一定の範囲を宣言的な記述でまかなうことで、プログラムを書くことを減らす意図のあるINTER-Mediatorではなんとかしたかったところです。そこで、フィールド単位のバリデーションが違反したら、「ダイアログボックスを表示して促す」「近辺に赤字等でメッセージを出す」という仕組みを定義ファイルの設定だけで実現しました。そして、イベント発生時に違反が検知されれば、雇用な動作をして、フィールドからフォーカスがはずれないようにしました。

しかし、ここまででも、すでに議論のポイントはいくつもあります。

  • バリデーションはいつ行うのか?
    • キータイプごと?
    • カット&ペーストするごと?
    • データベース更新前?
    • 複数のフィールドに対して「書き込み」ボタンがあれば、それを押したとき?
    • ポストOnlyモードとデータベース更新時は動作を違う必要があるのか?
    • 現状は、定義ファイルで指定可能なのはデータベース更新前のみ。
  • 違反通知をどのように行うのか?
    • 現在は、ダイアログボックスとページ上への文字の追加
    • 新しいページを表示したい?
    • 何が間違えたのかをもっと詳しく表示したい?
  • バリデーションに違反したら、その後にどのような操作を期待するのか?あるいは期待されるのか?
    • そのままでいいのか?
    • どこまでロールバックするのか?
    • どこまで既定値的な値を設定するのか?
    • 違反したレコードは削除しなくていいのか?
    • 違反してもレコードは作るのがいいのか?
    • 現在は、そのままにしつつ、正しい値を入力しないとそのページの他の作業をできなくしている。

あらゆるバリエーションに対する答えを用意するのか、それとも、主要な手法以外はプログラミングをしてもらうのか、その辺りが議論のポイントになると思います。いずれにしても、問題を書き出すことは必要でしょう。

データベースエンジンには通常バリデーションの仕組みは含まているので、本来はそちらを使うべきという議論があるでしょう。SQL言語での定義時に記述するため、「難しいから敬遠している」という向きもあるかもしれません。一方、なぜ、フレームワークがバリデーションをサポートするかというと、データベース側でのバリデーション処理は、違反時の状況の取得や、そこからの適切なユーザーインタフェースの構築、さらにやり直しなどの処理の組み立てなど、単純ではないプログラミングを要求されます。また、その対処方法も、データベースごとに違う可能性もあるので、むしろ、フレームワークの内部でバリデーションをサポートした方が、動作上作りやすいということもあるわけです。データベース側のエラー検知は、レイヤーを上下するワークフローをうまく組み立てるようなプログラミングを必要としますし、その結果、アプリケーションサーバーとデータベースということなるソフトウエア間の連携ということも必要になります。結果として、データベースに頼らない方が、フレームワーク内部で完結するため複雑さの一部を回避でき、加えてデータベースごとの事情に左右されないというメリットを生みます。

その結果、データベースのスキーマ定義にバリデーションルールが入らないことになり、それによる大きな問題は、初期値がバリデーション違反という状況で、ユーザーの操作に入ることがあります。これをどこの段階で、不正とみなし、どのような方法で排除するか、これが定まらないと、実装が揺らぎます。

ということで、とりあえず、頭にあることを書き出しておいて、議論を進める手掛かりにしたいと思います。

実は深刻な“カミFileMaker問題”

(2015/2/12 0:28更新:Facebookでの杉原さんの意見も反映させました。2015/2/13 17:00更新:Facebookでの日高さんの意見も反映させました。ありがとうございます。)

ネ申ExcelはExcelだけの問題か?

Excel方眼紙や罫線優先のワークシートなどなど、Excelで作った文書のうち、見栄えを整えることを主眼として作ってしまったことにより後からデータの活用ができなくなってしまった文書などを「ネ申Excel」と総称されています。三重大学の奥村先生の論文にまとめられています。良し悪しの問題は難しいのですが、例えばネ申Excelな記入用紙はWebフォームにすれば、即座に一覧が作られるなど、より利便性が高い代替え手法が明らかに存在するような場合があります。そのようにExcelにこだわる余り、自身の効率化を損なうのはもちろん、入力をするなどの利用者の利便性を著しく損なう状態になるのは避けるべきだと言えるかと思います。

同じような問題は他のソフトウエアにも、サービスにも存在します。ここで、データベースソフトの話をしたいのですが、そうなると、AccessかFileMakerかということになります。自分自身の問題でもあるので、馴染みの深いFileMakerを取り上げ、「カミFileMaker問題」として扱います。あえて、カタカナの「カミ」にしたのは、大昔「ファイルメーカー」と呼んでいたFileMakerへのトリビュートです。FileMakerのレイアウト機能は、帳票を高い自由度で作成できることから、従来の紙ベースで行われてきた業務をそのままデータベース化できる強い動機付けになっています。同じような仕様がAccessにもあることはありますが、プロの開発者からデータベース利用者までがみんなしてテーブルを定義してレイアウトをいじるというのはFileMaker市場の大きな特徴だけに、「カミFileMaker問題」が浮き彫りになると考えられます。

「カミFileMaker問題」とは?

ここで問題の定義として、紙の帳票類をそのままデータベース化してしまうことで発生する問題とします。もちろん、帳票をそのままデータベース化するだけでも、例えば、共有ができることや、印刷を省きパソコンやタブレットで閲覧することでの即時性などのメリットは発生します。最初の帳票ができたとき、「これはいい!」と誰もが思います。だけど、いくつも帳票を作るに従って問題が顕在化します。

紙が画面に変わっただけ?

帳票をデータベース化すると、どうしても従来のワークフローが結果的に踏襲されることになります。むしろ、ワークフローは出来上がっているのだから、それを電子化すればよりスムーズという、あまり根拠のない理屈を信じて突き進みます。その結果、まず、電子化されているのに「転記」という作業や、帳票の生成や破棄、あるいはステータス更新という動作に「手作業」が入ってしまいます。今までは紙だったので、それはやって当たり前でした。しかしながら、電子化するときに、そこでの自動化を深く考えなかったとしたら、同じことを端末を操作してやらないといけません。パソコンを扱うのが弱いとか、ストレスが高い人は、そこでまずはつまずくことになります。

紙にはなかったリンク情報はどうなった?

それでも仕事をみんなでがんばってこなしたとします。そして、さまざまな帳票レコードが、1つのエンティティに対して複数積み重なることにより、新たな問題が発生します。ある帳票と別の帳票が結びついている場合はよくあります。1対1や1対多ということもあるでしょうし、多対多もあります。紙で作業していると、その結びつきを人間がやっていることがあります。業務上の常識から、誰もが自動的に異なる2枚の帳票に関係がある、関係ないということを判定できるのです。この作業をパソコンの画面上で人間が行うのは非常に面倒です。紙のファイルを何冊か机に並べて対応するものを開けて突き合せるような手軽さは、今のパソコンやタブレットではまだまだ無理です。

もちろん、相手の文書IDを記録するなどすればいいのは当然ですが、気付いた時には遅かったということもあります。また、そのような「リンク」の重要性が、問題が発生しても気付かない、あるいは気付かないふりをするということあるでしょう。かくして、対応する文書の手がかりをかなり強引に引っ張り出すことになります。「同一顧客で、この帳票より前のもののうち、最新のもの」などといったリレーションシップを組むなどします。それでも、運用は可能かもしれませんが、対応が取れなくなる可能性が0ではなく、小さいながらも0%よりも大きくなります。また、業務で発生するイレギュラーなケースを忘れていると、ミスにつながります。ビジネスロジックでほぼ解決できるものでも、より単純な解決法があるのなら、単純な方が安定しているのは言うまでもありません。

矛盾する2つのフィールドの関係

さらにこんな例もあります。紙の帳票から、別の帳票に転記するとき、人間が転記するといいように解釈していたのですが、それを電子化すると、機械的に転記できないような場合が発生します。つまり、それぞれの帳票のスキーマに、論理的な不一致が発生してしまうのです。もっとも身近な例では、入力帳票が「氏名」で、それに対応する別の帳票が、姓名別々のフィールドのような場合です。こうしたスキーマの不一致は、紙の帳票をそのままデータベース化すると、気が付いたら行っていたということになります。また、転記なのか参照なのか、ここの考え所を全部転記することにしてしまったことで、かえって手作業が増えることもあります。

この問題は、突き詰めると、データベースのスキーマを検討するときに必ず必要となるその業務ドメインのモデル化が不正確なのです。フィールドとテーブルを定義すれば良いというのはFileMakerの機能のことで、設計者は他に考えないといけないことはたくさんあります。データ間の連携はもちろんだし、これについてはリレーションシップで定義することになります。しかしながら、FileMakerの機能に見えないこととして、あるデータは転記して見えるようにするのか、参照して見えるようにするのかということを考える必要があります。これは、FileMakerのテーブル設計のダイアログボックスでは明示化されていないものの、極めて重要な設計上の決まりごとになります。

データベースには、データベースに合った設計を

こうした問題は、昔からありますが、FileMakerは手軽だからこそ、陥る罠でもあるといえます。あえて、「カミFileMaker問題」として、問題を浮き彫りにすることで、より多くの方が意識できるようになればと考えました。もちろん、プロの方々もこうした失敗を重ねてきていて、いろいろなノウハウをお持ちでしょうし、長年使ってきた人も同様です。例えば、受託開発のとき、導入経験がないお客さんの要望をそのまま構築すると、「カミFileMaker問題」の宝庫です。プロや熟練者は、そこでしっかりとお客さんの要望を受け入れつつ、問題の発生を抑える手立てを考えないといけません。言われた通りにやればいいというのは間違いで、そのようなやり方を続けてきた結果使われないシステムを納品することになり、信用を落とすことになるのです。この問題を認識し、きちんと説明して方向付けをする必要があるのは、プロや熟練者の皆さんなのです。決して、素人を茶化すための問題定義ではないことは最後に強調したいと思います。

以下にまとめておきます。もちろん、みなさんの意見や表現の向上も大歓迎です。

「カミFileMaker問題」の発生

  • 帳票類をそのままFileMakerのデータベースで再現する電子化

「カミFileMaker問題」による起こりがちな結果

  • 一見すると効率化されたように見えるが、帳票作業とは異なる事務作業等が増えているなど、実は効率化されていない
  • 機械的に行えるデータの連携や解釈などが、データベースの仕様に含まれず、そうした作業も人間が行う必要がある
  • 2種類の帳票でスキーマが異なり、論理的には転記ができず、人間が解釈してつどつど修正しないといけなくなる
  • 紙が減ることの効率性だけに注目した結果、システム運用コストに気が回らず、気が付いたらかえってコスト高になってしまっていた
  • データベーススキーマの設計において正規化という点が考慮されず、データ解析に支障をきたしたり、あるいは解析自体が不可能な、品質の低いデータを蓄積してしまう。

「カミFileMaker問題」の本質

  • コンピュータ作業に最適なワークフローを検討し、転換しないために、結果的にさほどの効率化もされない
  • データ間のリンク構造を記録できないスキーマ上で、ビジネスデータが蓄積され、どうしようもなくなり、最悪はデータ活用に制限が出てきてしまう
  • データベースのスキーマを単に「紙にあった項目」のみで認識することで、ドメイン自体を表現するモデルの精度が低くなってしまう

[IM] MySQLでエンコードにはまったとき

INTER-Mediatorの話題です。こういうことがあるかどうは微妙かもしれませんが、私の郵便番号検索のサイトで、以下のような状況になりました。

  • 郵便番号検索のアプリケーション(A)はmysql_Connectで稼働している(あー、5.5ではついに書き直さないといかんなー)
  • UTF-8で動いているつもりだったが、MySQLで、エンコーディングの指定をしないままに動かしてしまっていた(これは失敗、でも、UTF-8でなんとなく動いている)
  • その上に、INTER-Mediatorで作った、書籍販売サイト(B)を動かす。これも、なんとなく動いている。郵便番号とは別のデータベースを作ったので、独立しているから問題なかった。
  • INTER-Mediatorでの郵便番号検索ページ(C)をいくつかの理由で立ち上げて、郵便番号データを読み込んでいるデータベースを利用して検索しようとしたら、文字化けた。

とまあ、要するに、2項目目が失敗なのですが、なんとなく動いているという状況下気づかなかったものの、最後の項目ではちゃんとやろうとして失敗に気づいたということです。my.cnfに、エンコードとしてUTF-8をつけると、(C)はきちんと動きました。それから、(A)もきちんと動きました。はいおしまいと思っていたら、(B)が文字化けます。つまり、(A)(B)(C)、同時に動かなくなったのです。なお、(C)の後追加した「character_set_client=utf8」が問題(かどうかも問題)のようで、これはmy.cnfから削除しました。(C)の発覚後、「character_set_server=utf8」も追加しましたが、最終的にはこれは残してあります。

結局どうしたか? (A)は、実は、「SET NAMES utf8」というSQLコマンドを発行して、うまく動かしていたのです。つまり、これを動かさないとうまく行かない大昔の状況のまま運用していて、エンコードの指定が適切でなかったことに気づいたわけです。(A)(B)(C)全部を動かすために結局何をしたかというと、INTER-Mediatorのアプリケーションで「SET NAMES utf8」を実行するようにしました。クエリーするたびにまず、このコマンドを実施して、SELECTを出すようにしました。これも、定義ファイルの設定でできます。こんな感じの定義ファイルです。scriptというキーワードは、FileMakerではよく使いますが、すべてのデータベースで利用可能です。

IM_Entry(
    array(
        array(
            'name' => 'postalcode',
            'view' => 'pcode',
            'records' => 50,
            'maxrecords' => 50,
            'paging' => true,
            'script' => array(
                array(
                    "db-operation"=>"load",
                    "situation"=>"pre",
                    "definition"=>"SET NAMES utf8"
                ),
            ),
        ),
    ),
    null,
    array('db-class' => 'PDO'),
    false
);

そういうことで、あっさりトラブル解決できました。まあ、でも、郵便番号サイトは、今シーズン終わったら、PHP 5.5か5.6で動くように、全面改修だな。

FileMaker 13で、XMLおよびXSLTによる出力

久しぶりにタイトルのような事をやりました。この記事のハイライトは、変数でXSLTファイルの位置を指定できるようになったものの、エラーが出るという事象に遭遇しますが、その対処方法が分かったということです。

XML出力は、FileMaker 5くらいからありますし、出力するXMLデータにXSLTを適用して、テキストやらHTMLやらを生成することができるので、ちまちまと文字列処理を書かなくてもいいので便利です。XSLTの習得が大変という話もありますが、今時この種の情報は「基本知識」でしょうから、勉強すればいいのです。私自身は幸いにも、今はなくなってしまったXSLTでのWeb公開の時代に、かなり勉強していたし実稼働させたソリューションもあったので、自分的にはXSLTはOKです。この記事では、XMLやXSLTあるいはそれらを利用するFileMakerの機能については詳細には説明しませんので、必要な方は予習をしてください。

現実にXML/XSLTを使うとき、当然ながらXMLはテーブルを出力するときのフォーマットなので、テーブルから生成されることになります。一方、XSLTはどうでしょうか? 確実は方法は、Webサーバ等に配置して、URLで指定することですが、データベースファイルと別の管理対象ができて不便です。そこで、XSLTのテキスト自体をテーブルのフィールドにテキストとして保存しておくことで、データベースと一体化して管理ができて便利です。ただし、そのXSLTのテキストを利用するときにはファイルにしなければなりません。現実にはスクリプトを組むとして、1フィールド、1レコードだけのタブ区切りで出力すれば、余計な “” などは含まれません。ただし、その1フィールドに、改行が入っていると、コード番号12のものに置き換えられます。後から変更するのは面倒なので、XSLTのテキストから改行を除去する計算式を設定した計算フィールドをエクスポートすればいいのです。もちろん、XSLT内では改行の有無で動作が変わらないにしなければなりません。

さて、そこで、このXSLTファイルをどこに保存するのかということですが、WindowsでもMacで稼働できるようするには、テンポラリフォルダがいいのではないでしょうかということで、次のようなスクリプトで変数にパスを入れることにしました。順当な方法かと思います。

スクリーンショット 2014-06-26 10.47.20

この変数$templateJの結果を利用して、XSLTのテキストをエクスポートします。前に説明したように、スクリプトで、1レコードのみを対象レコードとして、改行を省いたテキストのフィールドだけをタブ区切りで出力します。出力ファイルに、変数$templateJを指定します。

ここで、XSLTによる変換をスクリプトのどこかで組み込むとします。スクリプトステップの「レコードのエクスポート」で、「出力ファイルの指定」を行い、OKボタンを押すと、ファイルタイプが「XML」の場合はさらに次のダイアログボックスが表示されます。ここで「XSLスタイルシートを使用」を選択し、ファイルを指定するわけですが、「ファイル」だとダイアログボックスが出て来て、特定のファイルの指定が必要です。Get ( テンポラリパス ) は一定のパスではない模様ですし、Win/Macでパスは違います。なので、「計算」を選択して、前の変数$templateJを指定して、実際に動かしてみます。ボタンと計算式の文字列が重なっているところにFileMaker社のこの機能への姿勢が見て取れます。

スクリーンショット 2014-06-26 10.46.40

すると、XMLのエクスポートをするときに、次のようなエラーが出ます。SAX(XMLのパーサ)のエラーがそのまま見えていて意味が分かりにくいですが、ようするにファイルが見つからないということです。Windowsでは、実際に見つからなかったパスも見えています。

スクリーンショット 2014-06-26 10.49.33

変数$templateJで出力し、その直後に変数$templateJでファイル参照して見つからない。ようするにバグなのですが、バグで使えません、以上ということでは前に進みません。バグは直らない可能性もあるからです。

そこで、変数$templateJの内容を、MacとWindowsで調べ見ました。Macだと、「/Macintosh HD/var/tmp/….」みたいになっています。Windowsだと、「/C:/Users/msyk/….」となっています。まず、Macは、明らかにパスの最初のコンポーネントを取り去れば、存在可能なUNIXパスになります。Windowsは…ともかく、まず、XSLTのテキストをエクスポートした後、以下のような「変数を設定」スクリプトステップを追加して、最初のコンポーネントを省いてみました。

スクリーンショット 2014-06-26 10.46.58

Macでは、予想通り、それでエラーなく動きました。一方、Windowsは別の対策をしないといけないかと思ったのですが、なんと、ちゃんと動きました。UNIXみたいなパスの与え方でどうやら動くようです。

XSLTのTipsを少し書いておきましょう。関連レコードのフィールドは、TOさえあれば出力できると思っていたら、ポータルに展開しないと出力されません。ポータルがない場合は、ちゃんと警告メッセージを出して、最初のレコードの値だけが出力されると言われます。ということで、ポータル作成はさぼれません。

関連レコードのデータは、たとえば2つのフィールドがあるとして、関連レコードが3つあると、次のように出力します。このノードの上位ノードのタグはROWになります。

<COL><DATA>1</DATA><DATA>2</DATA><DATA>3</DATA></COL>
<COL><DATA>abc</DATA><DATA>def</DATA><DATA>ghi</DATA></COL>

上記の2つのフィールドの番号を1、2とします。ROWタグがカレントノードのとき、上記のデータを展開するのにfor-eachを使うのですが、同じポータルにある関連レコードがいくつかるとき、for-eachをそのうち1つのフィールドに対して行います。次のような感じです。ネームスペースの略号は、xsl、fmrとしています。

<xsl:for-each select="fmr:COL[1]/fmr:DATA">
    <div>
        <xsl:value-of select="." />
        <xsl:value-of select="../../fmr:COL[2]/fmr:DATA[position()]" />
    </div>
</xsl:for-each>

最初のfor-eachは普通にDATAタグが複数あるので、それを1つ1つ取り出します。3行目の「.」によって、1つ目のフィールドの現在のDATAのテキストを取り出すので、「1」「2」「3」という値が順次取り出されます。2つ目のフィールドは、4行目にあるように、XPATHの記述で、カレントノードからROWまで「../..」でさかのぼり、そこから、COL、DATA集合へと下ります。そして「position()」というのは関数です。for-eachでループしているときに、何番目なのかを示す値を返します。これで、「abc」「def」「ghi」という文字列が順次得られます。

最後に大サービスで(笑)、FileMakerのXML出力向け(ただし、FMRXMLRESULTスキーマ)の汎用的なテンプレートを書いておきましょう。コピペして、あとはデータの順序に合わせてFMPXMLRESULTやROWタグ要素に対応するテンプレートにHTML等で出力テンプレートを書くだけです。

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xhtml="http://www.w3.org/1999/xhtml"
    xmlns:fmr="http://www.filemaker.com/fmpxmlresult"
    exclude-result-prefixes="xhtml fmr">
<xsl:output encoding="UTF-8" method="xml" indent="yes"
   omit-xml-declaration="yes"/>

<xsl:template match="fmr:ERRORCODE | fmr:PRODUCT | fmr:METADATA">
</xsl:template>

<xsl:template match="fmr:FMPXMLRESULT">
    <html><body><table>
       <tr><th>Name</th><th>Activity</th></tr>
       <xsl:apply-templates />
    </table></body></html>
</xsl:template>

<xsl:template match="fmr:ROW">
    <tr>
        <td><xsl:value-of select="fmr:COL[3]/fmr:DATA[position()]" /></td>
        <td>
            <table>
                <xsl:for-each select="fmr:COL[1]/fmr:DATA">
                   <tr>
                       <td><xsl:value-of select="." /></td>
                       <td><xsl:value-of select="../../fmr:COL[2]/fmr:DATA[position()]" /></td>
                   </tr>
                </xsl:for-each>
             </table>
         </td>
    </tr>
</xsl:template>

FileMaker Server 13トラブル対策メモ

Mac OS X Mavericks+Server v3上で稼働させているFileMaker Server 13が、再起動後ちゃんと動かなくなりました。トラブル対策をして動くようになったので、「今度また再起動したとき」に慌てないように、メモっておきます。

そもそも、あるデータベースに対して、スケジュールでスクリプトを動かそうとしたのでした。そうしたら、正しいアカウントで認証しても実行できるスクリプトがないと言われました。そんなはずはありません。他のパネルを見てみると、公開しているデータベースの一覧には何も出ていないのに、FileMaker Proからはデータベースを開くことができます。つまり、一覧がおかしいということでしょうか。ここで、Admin-Console再起動をするか、サーバの再起動をするか、非常に迷ったのですが、いろいろあってサーバの再起動をしました。正確には、30分待っても落ちなかったのでディスクアクセスをしていないタイミングでリセットしました。

その後、起動はして、FileMaker Proからのデータベース接続はできましたが、Admin-Consoleは開かず(ログインパネルすら出ない)、そしてWeb公開も失敗しています。Webサーバのデーモンhttpdが1つも上がっていない。

まず、やってみたのは、以下のコマンドの入力です。Admin-Consoleにログイン可能な管理アカウントは、ユーザ名がadmin、パスワードはpassword(もちろん架空のもの)です。まず、ここまでで、Admin-Consoleは接続できるようになりました。

fmsadmin stop adminserver -u admin -p password
fmsadmin start adminserver -u admin -p password
fmsadmin stop wpe -u admin -p password
fmsadmin start wpe -u admin -p password

しかし、まだ、Webサーバがこれでも動きません。次に行ったのは、次のようなコマンドです。

cd /Library/FileMaker\ Server/HTTPServer/
sudo touch stop
sudo touch start

つまり、FileMaker ServerのディレクトリにあるHTTPServerというディレクトリの中のstopおよびstartというファイルの更新日時を現在にします。別の記事で紹介した通り、このファイルが変更されると、FileMaker ServerのWebサーバが停止あるいは開始します。ここまでやると、実際にWebサーバが動き出しました。

fmsadminによるwpeサービスのstart/stopは不要なのかもしれませんが、Webサーバのデーモンhttpdを強制的に動かさないといけないのかもしれません。