FMC2Goスケジューラーの任意のキーワードで検索させる仕組み


[FileMaker Advent Calendar 2015](http://www.adventar.org/calendars/865)の25日目

# FMC2Goスケジューラーの任意のキーワードで検索させる仕組み
「Utility」テーブルの「zgt_SearchText」フィールドに入力された任意のキーワードで検索を行う機能がどのように作りこまれているかを見てみる

## レイアウト上でフィールドを確認してみる
![151225-01.png](https://qiita-image-store.s3.amazonaws.com/0/89027/c4b6a31c-f9bd-7594-b628-991def2f8490.png)
このフィールドにはスクリプトトリガが設定されているようだ

## 設定されているスクリプトトリガを確認してみる
![151225-02.png](https://qiita-image-store.s3.amazonaws.com/0/89027/181821c5-3729-7630-afb2-68ed68a389e3.png)
「OnObjectExit」イベントが設定されているようだ

## 「OnObjectExit」イベントに紐付けされている「Search Schedule and Speaker」スクリプトを見てみる

### 最初のブロック
![151225-03.png](https://qiita-image-store.s3.amazonaws.com/0/89027/36f5149c-c021-2b50-c2ea-a394b685a661.png)

0. 結果を保存するグローバル変数「$$Result」を初期化
0. 検索するワードを保存するローカル変数「$SearchText」に「Utility」テーブルの「zgt_SearchText」フィールドの値を設定
0. 検索するワードが設定されていなければスクリプト終了
0. ポップオーバーを閉じる

まあありきたりの処理です

### 次のブロック
![151225-04.png](https://qiita-image-store.s3.amazonaws.com/0/89027/6f129a10-5f0d-3289-095c-731253db8a6b.png)

0. セッションの検索結果を保存するローカル変数「$ScheduleResult」にExecuteSQLの実行結果を設定

```
Let ( [
	~sql = "select " &
		"[" & GetFieldName ( Schedule::zz_cRecordType ) & "]," &
		"[" & GetFieldName ( Schedule::__kp_Schedule_ID ) & "]," &
		"[" & GetFieldName ( Schedule::Schedule_Name ) & "]," &
		"[" & GetFieldName ( Schedule::zz_cSpeakerName_lastFirst ) & "]," &
		"[" & GetFieldName ( Schedule::zz__schedule_sort_key ) & "]" &
	" from [Schedule] where [" & GetFieldName ( Schedule::zz_cTextForSearch ) & "] like ?" ;
	~text = "%" & $SearchText & "%" ;
	~query = ExecuteSQL ( MakeSQL ( ~sql ) ; "|,|" ; ¶ ; ~text )
] ;
	Case ( ~query ≠ "?" ; ~query )
) // Let
```

問い合わせ用のSQL文を生成して、ExecuteSQL関数を使って結果を得るところまで一発でやっていますね

GetFieldName
> GetFieldName
目的 
参照されているフィールドの完全修飾名を返します。
構文 
GetFieldName ( フィールド名 )
引数 
フィールド名 - フィールド オブジェクトまたはフィールド名を参照するテキスト式の評価

こっそりカスタム関数「MakeSQL」も仕事をしています
MakeSQL
![151225-07.png](https://qiita-image-store.s3.amazonaws.com/0/89027/8ec3b8e8-97ce-b1e0-714d-82fea676e146.png)

```
/* 2013.6.25
テキスト内の[と]をダブルクォートに変換して返す。
"::"が含まれる場合は"\".\""に変換
*/

Case (
Position ( テキスト ; "[" ; 1 ; 1 ) or
Position ( テキスト ; "]" ; 1 ; 1 ) ;
Substitute ( テキスト ; [ "::" ; "\".\"" ] ; [ "[" ; "\"" ] ; [ "]" ; "\"" ] ) ;
テキスト
) // Case
```

MakeSQL関数を通すことによって

```sql
select 
[Schedule::zz_cRecordType],
[Schedule::__kp_Schedule_ID],
[Schedule::Schedule_Name],
[Schedule::zz_cSpeakerName_lastFirst],
[Schedule::zz__schedule_sort_key]
 from [Schedule]
 where [Schedule::zz_cTextForSearch]
 like ?
```

が

```sql
select 
"Schedule.zz_cRecordType",
"Schedule.__kp_Schedule_ID",
"Schedule.Schedule_Name",
"Schedule.zz_cSpeakerName_lastFirst",
"Schedule.zz__schedule_sort_key"
 from "Schedule"
 where "Schedule.zz_cTextForSearch"
 like ?
```

となります
ExecuteSQL関数の

- 第1引数で問い合わせのためのSQL文
- 第2引数で"|,|"を列の区切りとする
- 第3引数で改行を行の区切りとする
- 第4引数で検索するワード(ここではLIKE句のパラメータとして)

を渡して問い合わせを実行し、その実行結果を変数「~query」で受けています
そして「Case ( ~query ≠ "?" ; ~query )」でExecuteSQL関数がエラーを返さなかったときだけ結果を返すようにしています

ExecuteSQL
> クエリー解析または実行時にエラーが発生した場合、FileMaker Pro は? を返します。

### 次のブロック
![151225-05.png](https://qiita-image-store.s3.amazonaws.com/0/89027/5764d833-171d-b04d-f924-672e8ce4aa2a.png)

0. スピーカーの検索結果を保存するローカル変数「$SpeakerResult」にExecuteSQLの実行結果を設定

```
Let ( [
	~sql = "select " &
		"[" & GetFieldName ( Speaker::zz_cRecordType ) & "]," &
		"[" & GetFieldName ( Speaker::__kp_SPK_ID ) & "]," &
		"[" & GetFieldName ( Speaker::_Speaker_LastFirst ) & "]," &
		"[" & GetFieldName ( Speaker::Speaker_Name_Kana ) & "]" &
	" from [Speaker] where [" & GetFieldName ( Speaker::zz_cTextForSearch ) & "] like ?" ;
	~text = "%" & $SearchText & "%" ;
	~query = ExecuteSQL ( MakeSQL ( ~sql ) ; "|,|" ; ¶ ; ~text )
] ;
	Case ( ~query ≠ "?" ; ~query )
) // Let
```

一応生成されるSQL文も見ておきましょう

```sql
select 
[Speaker::zz_cRecordType],
[Speaker::__kp_SPK_ID],
[Speaker::_Speaker_LastFirst],
[Speaker::Speaker_Name_Kana]
 from [Speaker]
 where [Speaker::zz_cTextForSearch]
 like ?
```

がMakeSQL関数で

```sql
select 
"Speaker.zz_cRecordType",
"Speaker.__kp_SPK_ID",
"Speaker._Speaker_LastFirst",
"Speaker.Speaker_Name_Kana"
 from "Speaker"
 where "Speaker.zz_cTextForSearch"
 like ?
```

となります

### 次のブロック
![151225-06.png](https://qiita-image-store.s3.amazonaws.com/0/89027/b82988f7-3fca-3815-f0c1-6b15ba994021.png)

0. セッションの検索結果「`$ScheduleResult`」とスピーカーの検索結果「`$SpeakerResult`」を結合して「$$Result」に設定  
※一般的なSQLでは「UNION句」で一発で処理しちゃうところです

### 最後のブロック
![151225-08.png](https://qiita-image-store.s3.amazonaws.com/0/89027/0dac3220-a612-26e9-88a3-d6cdffe07b4d.png)

0. Resultレイアウトに切り替え
0. 全レコードを表示
0. 該当件数を格納するローカル変数「`$FoundCount`」を設定(ValueCount ( $$Result ))
0. $FoundCount+1件目のレコードに移動する
0. 「複数レコードを対象外に」スクリプトステップで、移動先(`$FoundCount`+1件目)から(Get ( 対象レコード数 ) - $FoundCount)件のレコードを対象外にする
0. 「フィールド内容の全置換」スクリプトステップで、「Result」テーブルの「RecordNumber」フィールドを「シリアル番号」で全置換
0. 「フィールド内容の全置換」スクリプトステップで、「Result」テーブルの「kf_Result_ID」フィールドを「Result」テーブルの「zz_cFiled02」フィールドで全置換


複数レコードを対象外に
> 目的 
現在のレコードから指定された数の連続したレコードを対象レコードから除外してテーブル内の次のレコードに移動します。
構文 
複数レコードを対象外に [ダイアログなし; <レコード数>]

フィールド内容の全置換
> 目的 
現在のレコード内にある指定されたフィールドの値データまたは計算式によって求めた値を使用し、現在の対象レコード内にあるレコードすべてのフィールドにある値を置き換えます。
構文 
フィールド内容の全置換 [ダイアログなし; <テーブル::フィールド>;[現在の内容/シリアル番号/計算結果]

### 「Result」テーブルの「RecordNumber」フィールドを「シリアル番号」で全置換すると
フィールド内容の全置換
> オプション 	
[シリアル番号で置き換える] オプションを選択すると、現在の対象レコードにあるすべてのレコードのフィールドのシリアル番号が振り直されます。

### 「Result」テーブルの「kf_Result_ID」フィールドを「Result」テーブルの「zz_cFiled02」フィールドで全置換すると
![151225-09.png](https://qiita-image-store.s3.amazonaws.com/0/89027/31d0bc25-184a-ba9c-9547-49761933231f.png)
これらのリレーションによって「Schedule」テーブルと「Result_Speaker」テーブル(「Speaker」テーブルの別名)の情報が引っ張れるようになります

これらによって得られる結果が
![151225-10.png](https://qiita-image-store.s3.amazonaws.com/0/89027/7b027c12-d949-706c-90c0-2595f4cc05d5.png)
こうなります

でもこのスクリプトでは、どのテーブル、フィールドにも取ってきた検索結果をどこにも設定していないのに、検索結果が表示されていますね

これは「バーチャルリスト」という超絶技巧を利用して、テーブルオカレンスに縛られないデータ表示を実現しています
今回の場合はグローバル変数「$$Result」に入っている改行区切りテキスト(改行リスト)の情報を使って、「セッションの検索結果」と「スピーカーの検索結果」というもともと違うテーブルに入っているデータを同じレイアウトに直接表示させることができています

実は「Result」テーブルがその機能を担っていて、
![151225-11.png](https://qiita-image-store.s3.amazonaws.com/0/89027/816e7f07-d1c6-9ea9-0b4f-7275defc20bd.png)
のフィールド定義に仕込まれている計算式
![151225-12.png](https://qiita-image-store.s3.amazonaws.com/0/89027/7b723954-1521-77b9-0983-1967119226bb.png)
によって、グローバル変数「$$Result」に入っている改行区切りテキスト(改行リスト)の情報をレイアウト上で扱えるように転換しています

FileMakerは本来の仕様上、

- レイアウトはいずれかのテーブルオカレンスに束縛される
- レイアウトに表示できるフィールドは原則的に束縛されたテーブルオカレンスと、それとリレーションされているテーブルオカレンスに属するもののみ  
※「現在のテーブル」、「関連テーブル」以外のフィールドをレイアウトに置いてブラウズモードに切り替えた時「非関連テーブル」となって表示できないのはこの制限によるもの
- 但し「非関連テーブル」のフィールドでも「グローバル格納」になっているフィールド(グローバルフィールド)はこの制限に縛られない


となっています

[FileMaker Advent Calendar 2015](http://www.adventar.org/calendars/865)は今日でおしまいなはずでしたが、書きたいと思っていて書ききれなかったところがいくつかあるので、後日いくつかはフォローしていきます