An entity selection is an object containing one or more reference(s) to entities belonging to the same dataclass. An entity selection can contain 0, 1 or X entities from the dataclass -- where X can represent the total number of entities contained in the dataclass.
Entity selections can be:
"shareable" or "alterable",
"ordered" or "unordered".
These points are discussed below.
Entity selections are usually created using a query or returned from a relation attribute. For example:
brokers:=ds.Person.query("personType = broker")
This code returns in brokers all the people of type broker. To access an entity of the selection, use syntax similar to accessing an element in a collection. For example:
theBroker:=brokers[0] //Entity selections are 0 based
The entitySelection.orderBy( ) method returns a new entity selection according to the supplied sort criteria. For example:
brokers:=brokers.orderBy("name") //returns a sorted selection
This code returns in brokers the same entity selection of Person entities, but sorted by name.
Alternatively, you can use a relation attribute to return an entity selection. For example:
This code assigns to brokerCompanies all related companies of the people in the brokers entity selection, using the relation attribute myCompany. Using relation attributes on entity selections is a powerful and easy way to navigate up and down the chain of related entities.
To perform repeated actions to entities in an entity selection, like retrieving and modifying values of certain attributes, you can use the For each...End for each structure. For example:
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
Using a relation attribute of type "related entities" (see below).
You can simultaneously create and use as many different entity selections as you want for a dataclass. Keep in mind that an entity selection only contains references to entities. Different entity selections can contain references to the same entities.
Note: An entity selection can be used in different processes if it is shareable. For more information, see Shareable vs Alterable entity selections paragraph below.
All storage attributes (text, number, boolean, date) are available as properties of entity selections as well as entities. When used in conjunction with an entity selection, a scalar attribute returns a collection of scalar values. For example:
locals:=ds.Person.query("city = :1";"San Jose") //entity selection of people
localEmails:=locals.emailAddress //collection of email addresses (strings)
This code returns in localEmails a collection of email addresses as strings.
In addition to the variety of ways you can query, you can also use relation attributes as properties of entity selections to return new entity selections. For example, consider the following structure:
myParts:=ds.Part.query("ID < 100") //Return parts with ID less than 100 $myInvoices:=myParts.invoiceItems.invoice //All invoices with at least one line item related to a part in myParts
The last line will return in $myInvoices an entity selection of all invoices that have at least one invoice item related to a part in the entity selection myParts. When a relation attribute is used as a property of an entity selection, the result is always another entity selection, even if only one entity is returned. When a relation attribute is used as a property of an entity selection and no entities are returned, the result is an empty entity selection, not null.
An entity selection can be shareable (readable by multiple processes, but not alterable after creation) or alterable (supports the entitySelection.add( ) function, but only usable by the current process):
a shareable entity selection has the following characteristics:
it can be stored in a shared object or shared collection, and can be passed as parameter between several processes or workers;
it can be stored in several shared objects or collections, or in a shared object or collection which already belongs to a group (it does not have a locking identifier).
it does not allow the addition of new entities. Trying to add an entity to a shareable entity selection will trigger an error (1637 - This entity selection cannot be altered). To add an entity to a shareable entity selection, you must first transform it into a non-shareable entity selection using the entitySelection.copy( ) function, before calling entitySelection.add( ).
Note: Most entity selection functions (such as entitySelection.slice( ), entitySelection.and( )...) support shareable entity selections since they do not need to alter the original entity selection (they return a new one).
an alterable entity selection has the following characteristics:
it cannot be shared between processes, nor be stored in a shared object or collection. Trying to store a non-shareable entity selection in a shared object or collection will trigger an error (-10721 - Not supported value type in a shared object or shared collection)
it accepts the addition of new entities, i.e. it supports the entitySelection.add( ) function.
The shareable or alterable nature of an entity selection is defined when the entity selection is created (it cannot be modified afterwards). You can know the nature of an entity selection using the entitySelection.isAlterable( ) function or the OB Is shared command.
A new entity selection is shareable in the following cases:
the new entity selection is based upon a relation entity.{attributeName} (e.g. company.employees) when attributeName is a one-to-many related attribute and the entity does not belong to an entity selection.
the new entity selection is explicitely copied as shareable with entitySelection.copy( ) or OB Copy (i.e. with the ck shared option).
Example:
$myComp:=ds.Company.get(2) //$myComp does not belong to an entity selection $employees:=$myComp.employees //$employees is shareable
A new entity selection is alterable in the following cases:
the new entity selection is based upon a relation:
entity.{attributeName} (e.g. company.employees) when attributeName is a one-to-many related attribute and the entity belongs to an entity selection (same nature as entity.getSelection( ) entity selection)
entitySelection.{attributeName} (e.g. employees.employer) when attributeName is a one-to-many related attribute (same nature as the entity selection)
entitySelection.extract( ) when the resulting collection contains entity selections (same nature as the entity selection)
Examples:
$highSal:=ds.Employee.query("salary >= :1";1000000)&NBSP; //$highSal is shareable because of the query on dataClass $comp:=$highSal.employer //$comp is shareable because $highSal is shareable
$lowSal:=ds.Employee.query("salary <= :1";10000).copy() //$lowSal is alterable because of the copy() $comp2:=$lowSal.employer //$comp2 is alterable because $lowSal is alterable
Compatibility Note: It is possible to "force" all new entity selections to be alterable by default in your project using the dataStore.makeSelectionsAlterable( ) function. This compatibility setting is not recommended for new projects.
You work with two entity selections that you want to pass to a worker process so that it can send mails to appropriate persons:
var $paid;$unpaid : cs.InvoicesSelection //We get entity selections for paid and unpaid invoices $paid:=ds.Invoices.query("status=:1";"Paid") $unpaid:=ds.Invoices.query("status=:1";"Unpaid")
//We pass entity selection references as parameters to the worker CALL WORKER("mailing";"sendMails";$paid;$unpaid)
The sendMails method:
#DECLARE($paid cs.InvoicesSelection;$unpaid cs.InvoicesSelection)
var $invoice : cs.InvoicesEntity
//Loops on entity selections For each($invoice;$paid) $email.to:=$invoice.customer.address // email address of the customer $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 // email address of the customer $email.subject:="Please pay invoice # "+String($invoice.number) $status:=$transporter.send($email) End for each
Local entity selections can be ordered or unordered. Basically, both objects have similar functionality but there are some differences regarding their performance and available features. You can decide which type of entity selection to use according to your specific needs.
Note: The following entity selections are always ordered:
entity selections returned by 4D Server to a remote client
Unordered entity selections are built upon bit tables in memory. An unordered entity selection contains one bit for every entity in the dataclass, regardless of whether the entity is actually in the selection. Each bit is equal to 1 or 0, to indicate whether or not the entity is included in the selection. It is a very compact representation for each entity. As a consequence, operations using unordered entity selections are very fast. Also, unordered entity selections are very economical in terms of memory space. The size of an unordered entity selection, in bytes, is always equal to the total number of entities in the dataclass divided by 8. For example, if you create an unordered entity selection for a dataclass containing 10,000 entities, it takes up 1,250 bytes, which is about 1.2K in RAM. On the other hand, unordered entity selections cannot be ordered. You cannot rely on the position of entities within the selection. Also, you cannot have more than one reference to the same entity in the selection: each entity can only be added once.
Ordered entity selections are built upon longint arrays (containing entity references) in memory. Each reference to an entity takes 4 bytes in memory. Processing and maintaining such selections takes more time and requires more memory space than unsorted selections. On the other hand, they can be ordered or reordered, and you can rely on entity positions. Also, you can add more than one reference to the same entity.
The following table summarizes the main characteristics of each type of entity selection:
Feature
Unordered entity selection
Ordered entity selection
Processing speed
very fast
slower
Size in memory
very small
larger
Can contain several references to an entity
no
yes
For optimization reasons, by default 4D ORDA usually creates unordered entity selections, except when you use the orderBy( ) method or use the appropriate options (see below). In this documentation, unless specified, "entity selection" usually refers to an "unordered entity selection".
As mentioned above, by default ORDA creates and handles unordered entity selections as a result of operations such as queries or comparisons like and( ). Ordered entity selections are created only when necessary or when specifically requested using options.
Ordered entity selections are created in the following cases:
result of an orderBy( ) on a selection (of any type) or an orderBy( ) on a dataclass
result of the newSelection( ) method with the dk keep ordered option
Unordered entity selections are created in the following cases:
result of a standard query( ) on a selection (of any type) or a query( ) on a dataclass,
result of the newSelection( ) method without option,
result of any of the comparison methods, whatever the input selection types: or( ), and( ), minus( ).
Note that when an ordered entity selection becomes an unordered entity selection, any repeated entity references are removed.
If you want to transform an ordered entity selection to an unordered one, you can just apply an and( ) operation to it, for example:
//mySel is an ordered entity selection mySel:=mySel.and(mySel) //mySel is now an unordered entity selection
4D provides an automatic optimization for ORDA requests that use entity selections or load entities in client/server configurations. This optimization speeds up the execution of your 4D application by reducing drastically the volume of information transmitted over the network.
The following optimization mechanisms are implemented:
When a client requests an entity selection from the server, 4D automatically "learns" which attributes of the entity selection are actually used on the client side during the code execution, and builds a corresponding "optimization context". This context is attached to the entity selection and stores the used attributes. It will be dynamically updated if other attributes are used afterwards.
Subsequent requests sent to the server on the same entity selection automatically reuse the optimization context and only get necessary attributes from the server, which accelerates the processing. For example in an entity selection-based list box, the learning phase takes place during the display of the first rows, next rows display is very optimized.
An existing optimization context can be passed as a property to another entity selection of the same dataclass, thus bypassing the learning phase and accelerating the application (see "Using the context property" below).
The following methods automatically associate the optimization context of the source entity selection to the returned entity selection:
$sel:=$ds.Employee.query("firstname = ab@") For each($e;$sel) $s:=$e.firstname+" "+$e.lastname+" works for "+$e.employer.name // $e.employer refers to Company table End for each
Thanks to the optimization, this request will only get data from used attributes (firstname, lastname, employer, employer.name) in $sel after a learning phase.
Using the context property
You can increase the benefits of the optimization by using the context property. This property references an optimization context "learned" for an entity selection. It can be passed as parameter to ORDA methods that return new entity selections, so that entity selections directly request used attributes to the server and bypass the learning phase.
A same optimization context property can be passed to unlimited number of entity selections on the same dataclass. All ORDA methods that handle entity selections support the context property (for example dataClass.query( ) or dataClass.all( ) method). Keep in mind, however, that a context is automatically updated when new attributes are used in other parts of the code. Reusing the same context in different codes could result in overloading the context and then, reduce its efficiency.
Note: A similar mechanism is implemented for entities that are loaded, so that only used attributes are requested (see the dataClass.get( ) method).
$sel1:=ds.Employee.query("lastname = S@";$querysettings) $data:=extractData($sel1) // In extractData method an optimization is triggered and associated to context "shortList"
$sel2:=ds.Employee.query("lastname = Sm@";$querysettings) $data:=extractData($sel2) // In extractData method the optimization associated to context "shortList" is applied
$sel3:=ds.Employee.query("lastname = Smith";$querysettings2) $data:=extractDetailedData($sel3) // In extractDetailedData method an optimization is triggered and associated to context "longList"
$sel4:=ds.Employee.query("lastname = Brown";$querysettings2) $data:=extractDetailedData($sel4) // In extractDetailedData method the optimization associated to context "longList" is applied
Entity selection-based list box
Entity selection optimization is automatically applied to entity selection-based list boxes in client/server configurations, when displaying and scrolling a list box content: only the attributes displayed in the list box are requested from the server. A specific "page mode" context is also provided when loading the current entity through the Current item property expression of the list box (see Collection or entity selection type list boxes). This feature allows you to not overload the list box initial context in this case, especially if the "page" requests additional attributes. Note that only the use of Current item expression will create/use the page context (access through entitySelection[index] will alter the entity selection context). Subsequent requests to server sent by entity browsing methods will also support this optimization. The following methods automatically associate the optimization context of the source entity to the returned entity:
For example, the following code loads the selected entity and allows browsing in the entity selection. Entities are loaded in a separate context and the listbox initial context is left untouched:
$myEntity:=Form.currentElement //current item expression //... do something $myEntity:=$myEntity.next() //loads the next entity using the same context
Compatibility Note: Contexts handled in connections established through Open datastore can only be used between similar main versions of 4D. For example, a 4D v19.x remote application can only use contexts of a 4D Server v19.x datastore.
The entity selection properties are however enumerable:
ARRAY TEXT($prop;0) OB GET PROPERTY NAMES(ds.Employee.all();$prop) //$prop contains the names of the entity selection properties //("length", 00", "01"...)