Courierは,定番のクロスプラットフォーム等幅フォントですが,Windows版はビットマップフォントであることに注意する必要があります。Mac版であれば,Courierフォントが設定されたフォームオブジェクトを印刷することができますが,Windows版は,印刷にDirect2Dが使用されているため,別のフォントが出力されることになります。これは仕様です。
フォームエディターで「透過」プロパティが有効にされている場合,OBJECT SET RGB COLORS
で背景色が指定されたとしても,オブジェクトの背景は塗りつぶされません。背景色にはBackground color none
という定数がありますが,これは「透過」プロパティが有効にされていない場合にだけ使用できる値です。
一方,プロジェクトモードでは,フォームエディターの「透過」プロパティはエクスポートされず,背景色だけがエクスポートされます。したがって,フォームエディターで「透過」プロパティが有効にされているオブジェクトの背景色をプロジェクトモードで再現するためには,OBJECT SET RGB COLORS
で背景色をBackground color none
に指定する必要があります。これは仕様です。
なお,17r5では,OBJECT SET RGB COLORS
が改良され,背景色の指定を省略したり,前景色には触れずに背景色だけを変更できるようになりました(空の文字列を指定)。
ON ERROR CALL
でエラー処理メソッドを宣言する場合,戻り値のないメソッドを指定するようにしてください。エラー処理メソッドが$0
を宣言している場合,コンパイルモードでランタイムエラー-20002
(存在しないパラメーターに対するアクセス)が返されることになります。これは仕様です。
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を実行してもエラーが返されませんでした。しかし,正しい結果が返されたわけでもありません。