4D RemoteのORDA(ランゲージまたはリストボックス)の裏側では,RESTリクエストがサーバーに送られています。17r5では,クライアント側からのRESTリクエストを最適化することにより,ORDAのパフォーマンスを向上させるための努力が払われました。
公式ブログ記事(英文)で紹介されているように,17r5のORDAは,17r4との比較で200-300%(LAN)または3000%(WAN)のスピードが期待できます。リストボックスの場合,従来のカレントセレクション型との比較でTCPフレーム数が98.8%,転送バイトサイズが98.7%, 処理時間は88%ほど削減できるかもしれません。
Use ORDA to boost performance in Client/Server mode
具体的には,データクラス,そのデータクラスと階層に制限なくリレーションで結ばれているデータクラスに対するアクセスの仕方に修正が加えられました。オブジェクト型フィールドやピクチャに対するRESTアクセスは修正の対象外となっています。
17r4までは,エンティティにアクセスするようなコードがクライアント側で実行されるたびにRESTリクエストがサーバーに送信されていました。また,サーバーからは,データクラスの属性がすべて返されていました。リレーション属性を介して別のデータクラスのエンティティにアクセスするようなコードでも,同じようにRESTリクエストが発生しました。
17r5以降,同一のエンティティセレクションに属する複数のエンティティに対するアクセスが検出されると,コードが解析され,必要な属性だけがまとめてリクエストされるようになりました。
17r5以降,RESTリクエストの最適化は,デフォルトで有効にされており,エンティティセレクション型のリストボックスでも自動的に働くようになっています。たとえば,スクロール操作では,インタフェースに表示されているエンティティ属性だけがサーバーからまとめて返されます。
フォーミュラ等でエンティティをロードする場合,あるいはエンティティを更新する場合,最初の1個はすべての属性がロードされ,後続の処理では,実際に使用した属性だけがリクエストされます。
クライアント側のプロセスからメインのデータストア(ds
)にアクセスする場合,RESTリクエストの最適化コンテキスト(後述)を使用することができます。このコンテキストは,サーバーとの接続が中断されない限り,有効です。
ここではFor each
ブロックが使用されていますが,エンティティセレクションからエンティティを取り出すようなコードであれば,どのような処理でも最適化の対象となります。
$sel2:=$ds.Employee.query("firstname = ab@")
For each ($e;$sel2)
$s:=$e.firstname+" "+$e.lastname+" works for "+$e.employer.name
End for each
$sel2:=$ds.Employee.query("firstname = ab@")
127.0.0.1:8044/rest/Employee?$query='%7B%22model%22:%22Employee%22,%22queryItems%22:%5B%7B%22tokenType%22:%22simpleCompWithEm%22,%22attName%22:%22firstname%22,%22emName%22:%22Employee%22,%22comparaison%22:13,%22instance%22:0,%22checkForNull%22:false,%22value%22:%22ab@%22,%22diacritical%22:false%7D%5D%7D'&$method='entityset'&$progress4Dinfo='5CB39501D9854659885585A0E40A1127'&$top='80'
注記:エンティティセレクションのエンティティは,80個(既定値)ずつ返されます。
$e.employer.name
注記:Employee.employerはCompanyデータクラスです。
127.0.0.1:8044/rest/Company(2))
{__entityModel:Company,__KEY:2,__TIMESTAMP:2018-04-25T14:42:18.351Z,__STAMP:0,ID:2,name:Charlie Echo Animations,creationDate:0!0!0,revenues:82000000,extra:{a:false},employees:{__deferred:{uri:/rest/Company(2)/employees?$expand=employees}}}
ポイント:エンティティの属性がすべて返されます。
For each
ループ(2回目以降)
127.0.0.1:8044/rest/Employee(309)
{__entityModel:Employee,__KEY:309,__TIMESTAMP:2018-10-11T15:16:37.727Z,__STAMP:0,ID:309,firstname:Abarrane,lastname:BIASAL,salary:30709,birthdate:0!0!0,woman:true,managerID:307,employerID:2,photo:null,extra:null,heure:0,manager:{__deferred:{uri:/rest/Employee(307),__KEY:307}},employer:{__deferred:{uri:/rest/Company(2),__KEY:2}},directReports:{__deferred:{uri:/rest/Employee(309)/directReports?$expand=directReports}}}
ポイント:エンティティ毎にRESTリクエストが発生します。
$sel2:=$ds.Employee.query("firstname = ab@")
127.0.0.1:8044/rest/Employee?$query='%7B%22model%22:%22Employee%22,%22queryItems%22:%5B%7B%22tokenType%22:%22simpleCompWithEm%22,%22attName%22:%22firstname%22,%22emName%22:%22Employee%22,%22comparaison%22:13,%22instance%22:0,%22checkForNull%22:false,%22value%22:%22ab@%22,%22diacritical%22:false%7D%5D%7D'&$method='entityset'&$progress4Dinfo='5CB39501D9854659885585A0E40A1127'&$top='80'
127.0.0.1:8044/rest/Company(2))
{__entityModel:Company,__KEY:2,__TIMESTAMP:2018-04-25T14:42:18.351Z,__STAMP:0,ID:2,name:Charlie Echo Animations,creationDate:0!0!0,revenues:82000000,extra:{a:false},employees:{__deferred:{uri:/rest/Company(2)/employees?$expand=employees}}}
ポイント:ここまでは以前と変わりません。
エンティティの属性はすべてロードされましたが,For each
ブロックの中で,実際にアクセスしたのは firstname
lastname
employer.name
の3個だけでした。必要な属性が判明したので,ループの2回目では,エンティティセレクション内の2番目から81番目までのエンティティについて,必要な属性だけがまとめてリクエストされます。これが最適化です。
For each
ループ(2回目)
127.0.0.1:8044/rest/Employee/$entityset/E301FD6333524B30865981380A6257C1?$attributes='firstname,lastname,employer,employer.name'&$skip='1'&$top='80'
条件分岐により,ループ内でアクセスする属性が変動する場合はどうでしょうか。最適化で省略された属性に対するアクセスが途中で発生した場合,追加のリクエストがサーバーに送信され,次回の最適化されたORDAリクエストからは,その属性も「必要な属性」のリストに含まれます。
ds.private.setRemoteAutoFilter(False)
$toBeMarkedAsUsed:=$sel2.first().salary // If条件で必要になることがあるのでタッチ
For each ($e;$sel2)
$s:=$e.firstname+" "+$e.lastname+" works for "+$e.employer.name
If ($i%40=0)
$s:=$s+" and earns "+String($e.salary)
End if
End for each
Get database measures
にclientORDA
という新しいプロパティが追加されました。ORDAの最適化により,リクエストの回数・時間・サイズが節約されていることが確認できます。前述したように,ORDAの最適化は,RESTリクエストでロードするべきエンティティの「必要な属性」をクライアントが学習することにより,実現しています。クエリなどの処理によって形成された「最適化コンテキスト」は,同一エンティティセレクションに対する連続的なアクセスで暗黙的に使用されるだけでなく,他の場面でも明示的に使用できるようになっています。
新しいエンティティセレクションを返す下記のコマンドは,最適化コンテキスト(オブジェクト型)が渡せるようになりました。
dataClass.query()
entitySelection.query()
dataClass.fromCollection()
dataClass.all()
Create entity selection
注記: query()
は,すでにオブジェクト型のパラメーターquerySettings
をサポートしています。今回,このオブジェクトにcontext
というプロパティが追加されました。
parameters
queryPath
queryPlan
attributes
(17r5)
parameters
(17r5)
最適化コンテキストをサポートするコマンドには,context
プロパティにコンテキスト名(文字列)がセットされたオブジェクトを渡すことができます。同名のコンテキストが渡されたコマンドは,同一の最適化コンテキストを共有することになります。プロセスが違っていても,最適化コンテキストを共有することができますが,異なるデータクラスに対して同じコンテキストを使用することはできません。
下記のメンバーメソッドは,自動的にエンティティセレクションの最適化コンテキストを使用します。
entitySelection.and()
entitySelection.minus()
entitySelection.or()
entitySelection.orderBy()
entitySelection.slice()
entitySelection.drop()
エンティティセレクションに対する処理で形成された最適化コンテキストは,dataClass.get()
メソッドに渡すことができます。
dataClass.get(primaryKey; {settings} )
最適化コンテキストを使用するコマンドは,初回のアクセスで対象エンティティの属性をすべてリクエストします。その後のアクセスでは,実際に使用した属性だけがリクエストされ,適宜,「必要な属性」のリストに属性が追加されてゆきます。
最適化リクエストが管理している「必要な属性」のリストを返すコマンドのようなものはありませんが,17r6で予定されているORDAリクエストのログファイルに記録が残されます。もちろん,RESTリクエストのURLをキャプチャして特定することもできます。
スタイル付きテキストからST Get plain text
で標準テキストを取り出した場合,返されるテキストの改行コードはどちらのプラットフォームであってもCR
(\r
)に統一されます。つまり,Windows標準の改行コード(CR
LF
)の\n
は返されません。これは,テキストの長さや文字列の位置を合わせるための処置であり,仕様です。
4DのSQLは,暗黙的なLEFT JOIN
をサポートしていません。これは仕様です。たとえば,下記のコードはOUTER JOIN
をWHERE
で暗黙的に示唆しているので,「実装されていない機能です」というエラーが返されます。
Begin SQL
SELECT Employees.name, Employees.depID, Departments.depID, Departments.depName
FROM Employees
LEFT OUTER JOIN Departments
ON Employees.DepID = Departments.DepID
WHERE Employees.KeyID = Departments.KeyID
INTO :Lbx2;
End SQL
初期のバージョン(v13)では,このようなSQLを実行してもエラーが返されませんでした。しかし,正しい結果が返されたわけでもありません。
ネットワーク上に複数の4D Serverが同一の名前で公開されており,一方はストラクチャ設定の公開名,他方はユーザー設定の公開名をプロードキャストしている場合,Alt
/option
キーを押しながらクライアントを起動して「サーバーに接続」ダイアログを使用すれば,後者のサーバーに接続することができ,ユーザー設定のlastServer.xml
も更新されますが,次回,Alt
/option
キーを押さずに起動すると,クライアントはlastServer.xml
に記録されているほうのサーバーではなく,本来の公開名でプロードキャストされているほうのサーバーに接続します。これは仕様です。
lastServer.xml
は,ビルドされたクライアントアプリケーションがビルドされたサーバーアプリケーションをブロードキャスト公開名(検索サービス)ではみつけることができなかった場合の復帰手段として用意されています。ネットワーク上に複数の4D Serverが同一の名前で公開されているような環境では,ビルド時にBuildIPAdress
キーを指定して,IPアドレスまたはDNS名でサーバーを指定するようにしてください。
4D ViewドキュメントをView Proに変換した場合,日付型の差を求める計算式が設定されたセルの値が変わってしまうことがあります。たとえば,A1
セルに2019-01-01
,A2
セルに2019-01-06
が代入されている場合,A2-A1
は5
(正)となりますが,A3/2
は2
(誤)となります。これは仕様です。
4Dランゲージで日付の減算を実行すれば,日数が整数で返されますが,4D Viewの計算エンジンは特殊であり,日付の除算や乗算であっても,計算値が整数で返されます。日数を整数ではなく,実数で計算したいのであれば,明示的に0
を加算してください。前述の例でいえば,(A3+0)/2
は2.5
となります。逆に,4D Viewと同じように整数で計算したいのであれば,INT(A3/2)
と記述することができます。
DOM Parse XML source
またはDOM Parse XML variable
でXML文書をインポートし,XML SET OPTION
でXML indentation
オプションをXML no indentation
に設定してからDOM EXPORT TO VAR
またはDOM EXPORT TO FILE
を使用した場合,依存のXMLノードに施されているプリティプリントは除去されません。これは仕様です。インデントは,DOM Create XML Ref
で作成したノードに適用されるオプションです。
###'###'##0.00;-###'###'##0.00;
のようにアポストロフィ記号が含まれるフォーマットが設定された4D ViewドキュメントをView Proに変換した場合,フォーマット文字列のアポストロフィがカンマ記号になります。これは仕様です。桁区切りにアポストロフィ記号を使用するSpreadJSのカルチャー設定を作成するか,View Proに変換してから### ##0.00;[Red]- ### ##0.00
のようなシンタックスで同等の条件フォーマットを設定してください。
外部ODBCデータベースに対し,SQL LOGIN
でログインした場合,Begin SQL
End SQL
ブロックであれば,1回のセッション内で何度でも使用することができますが,SQL EXECUTE
のほうは,2回目のコールでエラー1412
(SQL文がすでに開かれている)が返されます。これは仕様です。
ODBCデータソースに対してSQL EXECUTE
を続けて実行するのであれば,まず SQL CANCEL LOAD
で前のSQLステートメントを閉じる必要があります。
SET PICTURE METADATA
コマンドを使用すれば,JPEGまたはTIFF形式のピクチャにタグ情報を書き込むことができます。
Windows版でTIFF画像のタグ情報をひとつずつ更新した場合,毎回の書き込みで画像の展開と再圧縮が実行されるため,画像のサイズにより,処理にかなりの時間を要します。これはWIC(Windows Imaging Component)の制限であり,仕様です。圧縮の回数を抑えるため,タグ情報は1回のコールで更新することが勧められています。
64ビット版では,リスト形式のサブフォームに表示されたレコードを1回だけクリックした場合,On Clicked
イベントに続いてOn Display Detail
イベントが発生するかもしれません。これは仕様です。
On Display Detail
は,レコードの選択状態が変化したときなど,リストフォームの再描画が必要になったときに発生するイベントであり,そのタイミングは,描画レイヤーをキャッシュする仕組みなど,プラットフォーム側の実装にも影響されるので,32ビット版と64ビット版では,多少の違いがあるかもしれません。