ここは旧式の4DドキュメントWebサイトです。最新のアップデートされたドキュメントを読むには新サイトをご利用下さい→ developer.4d.com

ホーム

 
4D v19
エンティティセレクション

エンティティセレクション  


 

概要  

エンティティセレクションとは、同じデータクラスに所属するエンティティへの一つ以上の参照を格納しているオブジェクトのことです。エンティティセレクションは、データクラスから0個、1個、あるいはX個のエンティティを格納することができます。ここでのXはデータクラスに格納されているエンティティの総数を表します。

エンティティセレクションには以下の区別があります:

  • "共有可能"または"追加可能"
  • "順列あり"あるいは"順列なし"

この点については以下に説明があります。

エンティティセレクションは一般的にクエリを使用して作成されるか、あるいはリレーション属性から返されます。例:

 brokers:=ds.Person.query("personType = broker")

このコードではbrokers にbroker タイプの人間が全て返されます。セレクションのエンティティにアクセスするためには、コレクション内の要素を昇順に調べるのに似ているシンタックスを使用します。例:

 theBroker:=brokers[0]  //エンティティセレクション内の0に基づいたもの

entitySelection.orderBy( ) メソッドは提供されたソート条件に基づいた新しいエンティティセレクションを返します。例:

 brokers:=brokers.orderBy("name") //ソートされたセレクションを返す

このコードはbrokers にPersonエンティティからのエンティティセレクションを返しますが、名前順で並べ替えされたものが返されます。

また、リレーション属性を使用してエンティティセレクションを返すことも可能です。例:

 brokers:=ds.Person.query("personType = broker")
 brokerCompanies:=brokers.myCompany

このコードは、myCompany リレーション属性を使用して、brokers エンティティセレクション内の人間にリレートされた全ての企業をbrokerCompanies に代入します。エンティティセレクションに対してリレーション属性を使用するのは、リレートされたエンティティの連鎖を上下に検索するための、強力かつ簡単な方法です。

エンティティセレクション内のエンティティに対して何か繰り返しの操作を実行する(例えば特定の属性の値を取得して変更する)場合、For each...End for each 構造を使用することができます。例:

 C_OBJECT(emp)
 For each(emp;ds.Employees.all())
    If(emp.Country="UK")
       emp.salary:=emp.salary*1,03
       emp.save()
    End if
 End for each

以下の方法を用いることで、エンティティセレクション型のオブジェクトを作成することができます:

  • データクラス内のエンティティをクエリする(dataClass.query( ) メソッド参照)
  • dataClass.all( ) メソッドを使用して、データクラス内のエンティティを全て選択する
  • Create entity selection コマンドを使用して4D テーブルのカレントセレクションに基づいてエンティティセレクションを作成する
  • dataClass.newSelection( ) メソッドを使用して空のエンティティコレクションオブジェクトを作成する
  • ORDA - エンティティセレクション テーマ内の様々なメソッドの中から、entitySelection.or( ) のように新しいエンティティセレクションを返すものを使用する
  • "related entities" 型のリレーション属性を使用する(以下参照)

データクラスに対して、異なるエンティティセレクションを好きなだけ同時に作成し、使用することができます。エンティティセレクションは、エンティティへの参照を格納しているに過ぎないという点に注意してください。異なるエンティティセレクションが同じエンティティへの参照を格納することも可能です。

注: エンティティセレクションはそれが作成されたプロセスにおいてのみ定義されます。ですから、例えばエンティティセレクションへの参照をインタープロセス変数に保存して、他のプロセスで使用するという使い方はできません。

注: エンティティセレクションは、それが共有可能であった場合には異なるプロセスで使用することができます。詳細な情報については、以下の共有可能なエンティティセレクション/追加可能のエンティティセレクション の段落を参照してください。

全てのストレージ属性(テキスト、数値、ブール、日付)はエンティティセレクションのプロパティ、あるいはエンティティのプロパティとして利用可能です。エンティティセレクションと組み合わせて使用した場合、スカラー属性はスカラー値のコレクションを返します。例:

 locals:=ds.Person.query("city = :1";"San Jose") //人間のエンティティセレクション
 localEmails:=locals.emailAddress //メールアドレス(文字列)のコレクション

このコードはlocalEmails 内に文字列としてのメールアドレスのコレクションを返します。

様々なクエリの方法に加えて、リレーション属性をエンティティセレクションのプロパティとして使用することで新しいエンティティセレクションを得ることもできます。例えば、以下のようなストラクチャーがある場合を考えます:

 myParts:=ds.Part.query("ID < 100") //ID が100未満のパーツを返す
 $myInvoices:=myParts.invoiceItems.invoice
  //myParts内のパーツにリレーとされている品物を少なくとも1行は持っている全ての請求書

最後の行は、myParts エンティティセレクション内のパーツにリレートしている商品が少なくとも1つは含まれている全ての請求書のエンティティセレクションを、$myInvoices 内に返します。リレーション属性がエンティティセレクションのプロパティとして使用されると、返される結果は、たとえ返されるエンティティが一つだけだとしても、常に新しいエンティティセレクションとなります。リレーション属性がエンティティセレクションのプロパティとして使用されてエンティティが何も返ってこない場合、返されるのは空のエンティティセレクションであり、null ではありません。

エンティティセレクションには共有可能(複数のプロセスから読み出し可能、ただし作成後編集不可)と追加可能(entitySelection.add( ) ファンクションをサポートする、ただしカレントプロセスでのみ使用可能)の2種類があります:

  • 共有可能なエンティティセレクションは以下のような特徴を持ちます:
    • 共有オブジェクトまたは共有コレクションに保存することが可能で、複数のプロセス間あるいはワーカー間で共有することができます。
    • 複数の共有オブジェクトまたは共有コレクションに保存することが可能で、あるいはグループに既に属している共有オブジェクトまたは共有コレクションに保存することも可能です(言い換えるとロック識別子を持っていないということです)。
    • 新たにエンティティを追加することはできません。共有可能なエンティティセレクションに対してエンティティを追加しようとした場合、エラーがトリガーされます(エラー1637 - このエンティティセレクションは編集不可です)。共有可能なエンティティセレクションに対してエンティティを追加したい場合、entitySelection.add( ) を呼び出す前にまず一度をentitySelection.copy( ) 使用して共有不可のエンティティセレクションへと変換する必要があります。

注: 大部分のエンティティセレクションファンクション(entitySelection.slice( )entitySelection.and( )...など)は共有可能エンティティセレクションをサポートしています。これらのファンクションはオリジナルのエンティティセレクションを編集しない(新しいものを返す)からです。

  • 追加可能なエンティティセレクションは以下のような特徴を持ちます:
    • プロセス間での共有はできません。また共有オブジェクト/コレクションへの保存もできません。共有不可のエンティティセレクションを共有オブジェクト/コレクションに保存しようとした場合、エラーがトリガーされます(エラー -10721 - 共有オブジェクトまたはコレクションにおいてサポートされる値の型ではありません)。
    • 新たにエンティティを追加することができます。言い換えると、entitySelection.add( ) ファンクションをサポートするということです。

エンティティセレクションが共有可能であるか追加可能であるかは、作成時に決定されます(後から変更することはできません)。entitySelection.isAlterable( ) ファンクションまたはOB Is shared コマンドを使用することでエンティティセレクションの性質を知ることができます。

以下の場合には、新規のエンティティセレクションは共有可能になります:

  • データクラスに対して適用されたORDA クラスファンクションの戻り値の新規エンティティセレクション: dataClass.all( )dataClass.fromCollection( )dataClass.query( )
  • あるエンティティがエンティティセレクションに所属しておらず、かつそのattributeName リレーション属性が1対Nリレート属性であるときのリレーションentity.{attributeName} (例: company.employees)に基づいた新規エンティティセレクション
  • entitySelection.copy( ) あるいは OB Copy を使用して明示的に共有可能であるとして(例: ck shared オプションを使用して)コピーされた新規のエンティティセレクション

例: 

 $myComp:=ds.Company.get(2) //$myComp はどのエンティティセレクションにも属していない
 $employees:=$myComp.employees //$employees は共有可能

以下の場合には、新規のエンティティセレクションは追加可能になります:

例:

 $toModify:=ds.Company.all().copy() //$toModify は追加可能

以下の場合には、新規のエンティティセレクションはオリジナルエンティティセレクションの性質を継承します:

  • 既存のエンティティセレクションに対して様々なORDA クラスファンクション(entitySelection.query( )entitySelection.slice( )、など)のどれか1つが適用された戻り値として返された新規のエンティティセレクション
  • 新規のエンティティセレクションが以下のようにリレーションに基づいている場合: 

例:

 $highSal:=ds.Employee.query("salary >= :1";1000000)&NBSP//$highSal はデータクラスに対するクエリの結果なので共有可能
 $comp:=$highSal.employer //$highSal が共有可能なので、$comp も共有可能
 
 $lowSal:=ds.Employee.query("salary <= :1";10000).copy()
  //$lowSal はcopy() を使用したので追加可能
 $comp2:=$lowSal.employer // $lowSal が追加可能なので、$comp2 も追加可能

互換性に関する注意: dataStore.makeSelectionsAlterable( ) メソッドを使用することで、プロジェクト内のすべての新規のエンティティセレクションを"強制的に"追加可能に設定することが可能です。この互換性設定は新規のプロジェクトに対しては推奨されていません。

二つのエンティティセレクションを使用し、それらをワーカープロセスに渡して適切な相手をメールを送信したい場合を考えます:

 var $paid;$unpaid : cs.InvoicesSelection</p><p> // 支払済と未払いの請求書についてのエンティティセレクションを取得
 $paid:=ds.Invoices.query("status=:1";"Paid")
 $unpaid:=ds.Invoices.query("status=:1";"Unpaid")
 
  // エンティティセレクションの参照を引数としてワーカーに渡す
 CALL WORKER("mailing";"sendMails";$paid;$unpaid)

sendMails メソッド:

 #DECLARE($paid cs.InvoicesSelection;$unpaid cs.InvoicesSelection)
 var $invoice : cs.InvoicesEntity
 
 var $server;$transporter;$email;$status : Object
 
  // Eメールを準備
 $server:=New object
 $server.host:="exchange.company.com"
 $server.user:="myName@company.com"
 $server.password:="my!!password"
 $transporter:=SMTP New transporter($server)
 $email:=New object
 $email.from:="myName@company.com"
 
  // エンティティセレクションに対してループ処理
 For each($invoice;$paid)
    $email.to:=$invoice.customer.address // 顧客のEメールアドレス
    $email.subject:="Payment OK for invoice # "+String($invoice.number)
    $status:=$transporter.send($email)
 End for each
 
 For each($invoice;$unpaid)
    $email.to:=$invoice.customer.address // 顧客のEメールアドレス
    $email.subject:="Please pay invoice # "+String($invoice.number)
    $status:=$transporter.send($email)
 End for each

ローカルなエンティティセレクションは順列ありあるいは順列なしのどちらかです。基本的にはどちらのオブジェクトも似たような機能を持ちますが、そのパフォーマンスと利用できる機能に少し違いがあります。必要に応じて、どちらのタイプのエンティティセレクションを使用したいかを選択して下さい。

注: 次のエンティティセレクションは常に順列ありとなります。

  • 4D Server からリモートのクライアントに返されるエンティティセレクション
  • リモートデータストアにおいて作成されるエンティティセレクション

  • 順列なしのエンティティセレクションはメモリ内のビットテーブルに基づいてビルドされています。順列なしのエンティティセレクションは、エンティティが実際にセレクション内にあるかどうかにかかわらず、データクラス内の各エンティティに対して1ビットを格納しています。各ビットは1か0に等しく、これはエンティティがセレクションに含まれているかを表します。いわば各エンティティのとてもコンパクトな表現と言えます。
    結果として、順列なしのエンティティセレクションを使用したオペレーションはとても速いです。また、順列なしのエンティティセレクションはメモリ空間という面でも経済的です。順列なしのエンティティセレクションのバイト単位のサイズは、データクラス内のエンティティの数を8で割ったものと必ず等しくなります。例えば、10,000 エンティティを格納しているデータクラスの順列なしのエンティティセレクションを作成した場合、サイズは1,250 バイトとなり、RAM内では1.2KB程度となります。
    その一方で、順列なしのエンティティセレクションを並べ替えることはできません。セレクション内のエンティティの位置を当てにすることはできないということです。また、セレクション内で同じエンティティに対して二つ以上の参照を持つことはできません。言い換えると、各エンティティは一度しか追加できないということです。
  • 順列ありのエンティティセレクションはメモリ内で(エンティティ参照を格納する)倍長整数配列に基づいてビルドされています。エンティティへの各参照はメモリ内で4バイトのサイズをとります。このようなセレクションを処理し維持することは、順列なしのセレクションより長い時間と大きいメモリ空間を必要とすることになります。
    その一方で、並べ替えをしたり再並び替えをすることが可能であり、エンティティの位置に依存することができます。また、同じエンティティに対して複数の参照を持つことも可能です。

以下の表は、それぞれのエンティティセレクションのタイプの主な特徴をまとめたものです:

機能順列なしのエンティティセレクション順列ありエンティティセレクション
処理スピードとても速い遅い
メモリ内のサイズとても小さい大きい
エンティティに対して複数の参照を格納する不可可能

最適化の観点から、4D ORDA ではデフォルトでは通常順列なしのエンティティセレクションを作成しますが、orderBy( ) メソッドを使用した場合あるいは適切なオプション(以下参照)を使用した場合は除きます。このドキュメントでは、指定されている場合を除き、"エンティティセレクション"は"順列なしのエンティティセレクション"を指すこととします。

上記で書かれているように、デフォルトでは、ORDA はクエリやand( ) などの比較のオペレーションの結果として、順列なしのエンティティセレクションを作成・管理します。 順列ありのエンティティセレクションは、必要な場合において、あるいはオプションを使用して特別に要求した場合に限り作成されます。

順列ありのエンティティセレクションは以下のような場合に作成されます:

  • セレクション(タイプを問わず)に対してorderBy( ) をした、あるいはデータクラスに対してorderBy( ) をした戻り値
  • newSelection( ) メソッドにdk keep ordered オプションを渡した戻り値

順列なしのエンティティセレクションは以下のような場合に作成されます:

  • セレクション(タイプを問わず)に対して標準のquery( ) をした、あるいはデータクラスに対してquery( ) をした戻り値
  • オプションを使用しないでnewSelection( ) メソッドを使用した戻り値
  • 任意の比較メソッドの結果。入力セレクションタイプは問いません: or( ), and( ), minus( )

順列ありのエンティティセレクションが順列なしのエンティティセレクションになった場合、重複したエンティティ参照は全て削除されます。

順列ありのエンティティセレクションを順列なしのものに変換したい場合、そのエンティティセレクションに対してand( ) オペレーションを適用するだけです。例:

  //mySel は順列ありのエンティティセレクション
 mySel:=mySel.and(mySel)
  //これでmySel は順列なしのエンティティセレクションとなった

4D では、クライアント/サーバー環境においてエンティティセレクションを使用する、あるいはエンティティを読み込むORDAのリクエストを自動的に最適化する機構を提供しています。この最適化機構は、ネットワーク間でやり取りされるデータの量を大幅に縮小させることで4D の実行速度を向上させます。

この機能には、以下の最適化機構が実装されています:

  • クライアントがサーバーに対してエンティティセレクションのリクエストを送ると、4D はコード実行の途中で、エンティティセレクションのどの属性がクライアント側で実際に使用されているかを自動的に"学習"し、それに対応した"最適化コンテキスト"をビルドします。このコンテキストはエンティティセレクションに付随し、使用された属性を保存していきます。他の属性があとで使用された場合には自動的に情報を更新していきます。
  • サーバー上の同じエンティティセレクションに対してその後に送られたリクエストは、最適化コンテキストを再利用して、サーバーから必要な属性のみを取得していくことで、処理を早くします。例えば、エンティティセレクション型のリストボックスにおいては、"学習"フェーズは最初の行を表示中に行われるので、次の行の表示から最適化が行われます。
  • 既存の最適化コンテキストは、同じデータクラスの他のエンティティセレクションであればプロパティとして渡すことができるので、学習フェーズを省略して、アプリケーションをより早く実行することができます(以下の"コンテキストプロパティの使用"を参照してください)。

以下のメソッドは、返されるエンティティセレクションには、ソースのエンティティセレクションの最適化コンテキストを自動的に付随させます:

例題

以下のようなコードがあるとき:

 $sel:=$ds.Employee.query("firstname = ab@")
 For each($e;$sel)
    $s:=$e.firstname+" "+$e.lastname+" works for "+$e.employer.name // $e.employer はCompany テーブルを参照する
 End for each

最適化機構のおかげで、このリクエストは学習フェーズ以降は、$sel の中で実際に使用されている属性(firstname, lastname, employer, employer.name)からのみデータを取得するようになります。

 

contextプロパティの使用

context プロパティを使用することで、最適化の利点をさらに増幅させることができます。このプロパティはあるエンティティセレクションで"学習した"最適化コンテキストを参照します。これを新しいエンティティセレクションを返すORDA メソッドに引数として渡すことで、その返されたエンティティセレクションでは学習フェーズを最初から省略してサーバーに使用された属性をリクエストすることができるようになります。

同じ最適化context プロパティは、同じデータクラスのエンティティセレクションに対してであればどのエンティティセレクションにも渡すことができます。エンティティセレクションを扱う全てのORDA メソッドは、context プロパティをサポートします(例えばdataClass.query( ) あるいは dataClass.all( ) メソッドなど)。ただし、 コンテキストはコードの他の部分で新しい属性が使用された際には自動的に更新されるという点に注意してください。同じコンテキストを異なるコードで再利用しすぎると、コンテキストを読み込み過ぎて、結果として効率が落ちる可能性があります。

注: 同様の機構は読み込まれたエンティティにも実装されており、それによって使用した属性のみがリクエストされるようになります(dataClass.get( ) メソッド参照)。

 

dataClass.query( ) メソッドを使用した例:

 C_OBJECT($sel1;$sel2;$sel3;$sel4;$querysettings;$querysettings2)
 C_COLLECTION($data)
 $querysettings:=New object("context";"shortList")
 $querysettings2:=New object("context";"longList")
 
 $sel1:=ds.Employee.query("lastname = S@";$querysettings)
 $data:=extractData($sel1// extractData メソッドにおいて、最適化はトリガーされており、"shortList" のコンテキストが割り当てられています。
 
 $sel2:=ds.Employee.query("lastname = Sm@";$querysettings)
 $data:=extractData($sel2// extractData メソッドにおいて、"shortList" のコンテキストに割り当てられている最適化が適用されます。
 
 $sel3:=ds.Employee.query("lastname = Smith";$querysettings2)
 $data:=extractDetailedData($sel3// extractDetailedData メソッドにおいて、最適化はトリガーされており、"longList" のコンテキストが割り当てられています。
 
 $sel4:=ds.Employee.query("lastname = Brown";$querysettings2)
 $data:=extractDetailedData($sel4// extractDetailedData メソッドにおいて、"longList" のコンテキストに割り当てられている最適化が適用されます。

 

エンティティセレクション型リストボックス

クライアント/サーバー環境におけるエンティティセレクション型リストボックスにおいては、そのコンテンツを表示またはスクロールする際に、最適化が自動的に適用されます。つまり、リストボックスに表示されている属性のみがサーバーにリクエストされます。
また、リストボックスのカレント項目プロパティ式 (コレクション/エンティティセレクション型リストボックス参照) を介してカレントエンティティをロードする場合には、新たな"ページモード" コンテキストが提供されます。これによって、"ページ" による追加の属性リクエストが発生しても、リストボックスの初期コンテキストのオーバーロードが避けられます。なお、ページコンテキストの生成/使用はカレント項目式を使用した場合に限ります (例えば、entitySelection[index] を介してアクセスした場合は、エンティティセレクションコンテキストが変化します)。
その後、エンティティを走査するメソッドがサーバーに送信するリクエストにも、同じ最適化が適用されます。次のメソッドは、ソースエンティティの最適化コンテキストを、戻り値のエンティティに自動的に適用します:

たとえば、次のコードは選択したエンティティをロードし、所属のエンティティセレクションを走査します。エンティティは独自のコンテキストにロードされ、リストボックスの初期コンテキストは影響されません:

 $myEntity:=Form.currentElement // カレント項目式
  //... なんらかの処理
 $myEntity:=$myEntity.next() // 次のエンティティは同じコンテキストを使用してロードされます

互換性に関する注意: Open datastore を通して確立された接続で扱うことのできるコンテキストは、4Dのメインバージョンが同じもの同士でしか使用することができません。例えば、4D v19.x リモートアプリケーションは、4D Server v19.x データストアのコンテキストしか使うことができません。

エンティティセレクションオブジェクト自身は、オブジェクトとしてコピーすることはできません:

 $myentitysel:=OB Copy(ds.Employee.all()) //nullを返す

しかしながら、エンティティセレクションのプロパティは取得可能です:

 ARRAY TEXT($prop;0)
 OB GET PROPERTY NAMES(ds.Employee.all();$prop)
  //$prop にはエンティティセレクションプロパティの名前が格納される
  //("length", 00", "01"...)

 
プロパティ 

プロダクト: 4D
テーマ: ORDA

 
ページの目次 
 
履歴 

初出: 4D v17
変更: 4D v18 R5

 
ARTICLE USAGE

デザインリファレンス ( 4D v19)