FileMaker で作るメディア管理データベースで採用しているフィルタ機能について、仕組みやインターフェイスの詳細です。
はじめに
この投稿は FileMakerメディア管理 作り方 R9 以降を補完する内容でもあります。ここで取り上げるフィルタ機能は、添付の実作ファイル「MediaDB」の機能に準じます。
フィルタの機能
フィルタ機能とは何かというと、単に検索です。しかし、それらしいインターフェイスの実装によって、既存値で絞り込んだという感覚が得られますので、フィルタと言うております。
それらしいインターフェイスとは、ここでは具体的に MediaDB R9 の UIです。
画面の左側にフィルタ用のペインがあって、フィルタできる分類のラベルがリストされています。
分類ラベルは折り畳みトグルになっていて、展開するとデータベース内で既存の値がリストされます。値のクリックで検索されます。
現状では検索が三種類あります。
一つは、値のみで単純な検索。検索結果が入れ替わります。
一つは、値を追加して「いずれかを含む」の or 検索。結果が増えていきます。
一つは、値を絞り込んで「すべて含む」の and 検索。結果が減っていきます。
この仕組みについての解説が本投稿になります。
フィルタの仕組み 概要
ざっくりこういうことです。
- フィルタ分類を定義する – 値一覧を作成
- データを作成 – 値一覧を専用テーブルにレコード化
- UI – ポータルに配置して表示
- 検索 – ボタンに検索するスクリプトをセット
それぞれに細かい工夫を忍ばせます。以下、
フィルタ分類を定義する – 値一覧を作成
フィルタするのは既存のフィールドです。例えば「拡張子」というフィールドを、そのフィールド内容である「jpg」や「png」で検索します。これがフィルタ機能の基本的な仕組みですが、普通の検索と何が違うかというと、UI を使用するかどうかです。
ここでは UI に出現させるフィールドを「フィルタ分類」、フィールド内容を「値」と呼びます。まず最初に、フィルタ分類を作成することからです。特定一意のフィールドをフィルタ分類として定義します。
フィルタ分類として定義する方法とは、ズバリ値一覧を作ることです。
ルールがひとつだけあって、フィールドのフルネーム「テーブル名::フィールド名」を値一覧の名前に付けます。
このルールに沿って値一覧を作成することで UI に出現する分類として定義されます。
UI にフィルタ分類を表示したり取り除いたりしたい場合は、値一覧の名前を変更します。フルフィールドの書式でない名前はリストする際に無視されます。
データを作成 – 値一覧を専用テーブルにレコード化
filterItemsという名の専用テーブルを追加しています。このテーブルはメインブラウザにポータルで表示させます。UI の基礎となるテーブルです。filterItems には、フィルタ分類として定義した値一覧の値をレコード化します。
filterItems テーブルは値一覧が変更されるたびに作り直します。頻繁に更新し、メインのレコードと整合性を保つよう勤めます。
そのため、レコードを作成する全自動スクリプト作っておきます。値一覧の値からレコードを作成し、ポータルを更新するところまでを受け持ちます。
メインデータベースで新規レコードが作られたりキーワードが変更されたり、何か変更がなされる際に、この作成スクリプトを最後に発動させます。スクリプトの詳細の前に、filterItems テーブルについて少々。
filterItemsテーブルのフィールド
このテーブルの主なフィールドは次の通りです。
- key … 分類名。値一覧の名前が入ります。例えば「mainData::拡張子」
- value … 値、検索する項目名。値一覧の値が入ります。例えば「jpg」
- label … key のフィールド名。表示用。例えば「拡張子」
- select … 選択されれば 1 が入る数字フィールド
- recN … get(レコード番号)のレコード番号フィールド
他に、グローバルなフィールドがいくつかあります。
- ラベル行OPENリスト … ポータルで現在展開表示されている key のリスト
- 選択済みリスト … フィルタとして指定済み(select が 1)の key と value のリスト
- mainDataFileName … 照合用。メインデータのファイル名
ポータルでの表示を前提に、特にグローバルなフィールドで「何これ?」ってのがあるかと思います。ちょっと補足しておきます。
ラベル行OPENリストは、ポータルで折りたたみ表示を司ります。分類名で折りたたむ機能を実装しており、現在開いているkeyをリストしています。ていうか、ここにリストされた key が、展開表示されます。
選択済みリストは、選択しフィルタされている分類と値のリストが作られます。
select数は、selestの集計で合計値です。つまり、何もセレクトされていないと0です。1以上なら「何かでフィルタされている状態」と判ります。
mainDataFileName はフィールド内容が「mainData」固定であり、メインデータファイル mainData.fmp12 とファイル名でリレーションするためだけにあります。つまりこれは、照合も何も、ポータルで無条件に全部表示できるためだけにあります。
filterItems にレコードを作成する
さてこのテーブルにレコードを作成するスクリプトです。全自動で作ります。このスクリプトは「FilterPortalの更新」として、いろんな他のスクリプトから頻繁に使用されることにもなります。
丁寧に説明すると長くなるのでざっくりいきます。
まず最初に filterItems のレコードを全部削除してから始めます。
- すべての値一覧名をリストとしてゲットしてループ
- 名前が「テーブル名::フィールド名」だけ以下の処理
- $key に名前、$label にフィールド名、$vals に値一覧名の値を全部ゲット → 改行をカンマ区切りに変換
- 以上をタブ区切りの1行に納める
- リスト$fList に上記1行を追加する
- fList の行をループ
- GetValue して行から $key, $label, $vals を得る
- $vals の先頭にカンマを加えてからカンマを改行に変換してリスト化
- $valsをループ
- GetValue して $vals から $val を取得
- 新規レコード作成
- $key, $label, $val をフィールドにセット
こうして値一覧の名前と値がfilterItemsにすべてレコード化されました。
最初の行は value が空
この中で注目ポイントが一カ所あります。分類の最初の1個目のみ、value が空の状態でレコードが作られます。
分類の最初の1個だけ value が空である意味は、この1行目が、ポータルの折りたたみ部分、分類名タイトルと呼べる行になります。
UI – ポータルに配置して表示
filterItems テーブルのレコードをメインブラウザにポータルで表示します。ポータルには必ず名前を付けておきましょう。ここでは、filterPortal という名前です。
ポータル filterPortal 折り畳み表現
ポータル filterPortal のフィルターをこのように設定します。
IsEmpty ( filterItems::value ) or not IsEmpty ( FilterValues ( filterItems::ラベル行OPENリスト ; filterItems::key ) )
value が空であるか、またはグローバルフィールド「ラベル行OPENリスト」に key が含まれるレコードのみ表示せよと指定しています。
上の filterItemsテーブルのレコード作成で判るとおり、value が空のレコードは分類タイトルという意味が付与されます。
value が空の行(つまり分類タイトル行)にのみ表示されるボタンが配置されていて、クリックすると「ラベル行OPENリスト」にkeyがトグルで追加または削除されるスクリプトが割り当てられています。
「ラベル行OPENリスト」に何もない場合、分類タイトル行のみがポータルに表示されます。折り畳まれた状態です。
タイトルクリックにより key が「ラベル行OPENリスト」に追加されると、その key を持つレコードがすべて表示されます。展開された状態です。
この折りたたみの仕組み、ちょっとイカしてないですか?
レイアウトモードで見てみましょう。
ポータルの行にはkeyとvalueのフィールドを少しずらしただけの重ねた状態で配置しています。ボタンバーですからラベルも計算式で書けまるので ▼ の方向を変えられますね。
If ( not IsEmpty ( filterItems::value ) ; "" ; If ( not IsEmpty ( FilterValues ( filterItems::ラベル行OPENリスト ; filterItems::key ) ) ; "▼" ; "►" ) )
ボタンバーをいくつか置いて、「隠す」を駆使してタイトル行と値の行を分けています。タイトル行では「OPENに追加・削除のトグル」ボタン、値の行では「その値で検索」ボタンが現れています(透明ですが)
行の右側には別の小さなボタンがあり、二つのボタンが交互に現れます。+ ボタンは「追加」、× ボタンは「絞り込み」の検索となります。この二つのボタンの切り替えはポータル上部のボタンで行います。
ここはちょっとスマートと言えないUIデザインだと思っていて、今後の改善が望まれます。この場所でそもそも「絞り込み」が必要なのか?とちょっと思っていますが結論出ていません。
検索 – ボタンに検索するスクリプトをセット
filterPortal の行には以上のように折りたたみの表現や三種類の検索が詰め込まれています。整理を兼ねて、リストしてみましょう。ポータル行に配置されたフィールドやボタンを下から順に書いていきます。
フィールド label と value
フィールドは「label」と「value」が配置されています。value は、label から少しずらして、親子関係み見えるような位置におきます。label には隠す設定がされていて、タイトルとしてひとつだけが表示されるようになってます。
filterItems::recN ≠ 1 and filterItems::label = GetNthRecord ( filterItems::label ; filterItems::recN - 1 )
filterItemsフィールドには recN というレコード番号のフィールドがあります。「レコード1ではない」かつ「一個前のレコードとラベルが同じ」なら非表示になります。
ラベル展開ボタン
折りたたみの展開ボタン(透明)が行を覆っています。折りたたみボタンですから分類タイトルの行にだけ表示させます。隠す設定で、 value があれば非表示に設定しています。
このボタンに割り当てられているスクリプト「ラベル展開ボタン」は、クリックされた行の key を取得し、フィールド「ラベル行OPENリスト」になければ追加、あれば取り除くという動作を行います。いわば、OPENを付与する・外す という行為です。
value検索ボタン
こちらも透明で行を覆うボタンです。value行にのみ表示させるので、隠す設定で value が空の時は非表示に指定しています。
このボタンに割り当てられたスクリプト「value単独検索」は、単純な検索です。こちらもトグルで、再び同じ行がクリックされれば検索を解除します。検索自体は単純ですが、このスクリプトに留まらず、あらゆる検索のスクリプトでは検索実行の前後にごちゃごちゃと処理が挟まります。例えばポータル内ですでに何かが選択されていれば解除するとか、検索窓に何かあれば消しておくとか、いろいろあります。
追加と絞り込みの検索ボタン
行の右側には追加と絞り込みの二つのボタンが重ねて置いてあり、別のボタンで追加か絞り込みかを指定することで表示が入れ替わります。
追加ボタンでは、選択された value を検索条件に追加していく処理を行います。
絞り込みボタンでは、選択されたvalueの最初の1個は普通に検索し、2個目以上は「絞り込み」を実行させます。