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回のコールで更新することが勧められています。