Une sélection d'entités est un objet contenant une ou plusieurs référence(s) à des entités appartenant à la même dataclass. Une sélection d'entités peut contenir 0, 1 ou X entités de la dataclass - où X peut représenter le nombre total d'entités contenues dans la dataclass.
Les sélections d'entités peuvent être :
"partageables" ou "altérables"
"ordonnées" ou "non ordonnées"
Ces points sont examinés ci-dessous.
Les sélections d'entités sont généralement créées à l'aide d'une requête ou renvoyés à partir d'un attribut relationnel. Par exemple :
brokers:=ds.Person.query("personType = broker")
Ce code renvoie dans brokers toutes les personnes de type courtier. Pour accéder à une entité de la sélection, utilisez une syntaxe similaire à l'accès à un élément dans une collection. Par exemple :
theBroker:=brokers[0] //Les sélections d'entités sont basées sur 0
La méthode entitySelection.orderBy( ) retourne une nouvelle sélection d'entités en fonction de critères de tri fournis. Par exemple :
brokers:=brokers.orderBy("name") //renvoie une sélection triée
Ce code renvoie dans brokers la même sélection d'entités de Person, mais triée par nom.
Vous pouvez également utiliser un attribut relationnel pour renvoyer une sélection d'entités. Par exemple :
Ce code assigne à brokerCompanies toutes les sociétés liées à des personnes de la sélection d'entités brokers, en utilisant l'attribut relationnel myCompany. L'utilisation des attributs relationnels sur les sélections d'entités est un moyen puissant et facile de naviguer dans la chaîne des entités associées.
Pour effectuer des actions répétées sur des entités dans une sélection d'entités, comme récupérer et modifier des valeurs de certains attributs, vous pouvez utiliser la structure Pour chaque...Fin de chaque. Par exemple :
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
En utilisant un attribut de type "related entities" (voir ci-dessous).
Vous pouvez simultanément créer et utiliser autant de sélection d'entités que vous le souhaitez pour une dataclass. Gardez à l'esprit qu'une sélection d'entités contient uniquement des références à des entités. Des sélections d'entités différentes peuvent contenir des références aux mêmes entités.
Note : Une sélection d'entités peut être utilisée dans différents process si elle est partageable. Pour plus d'informations, reportez-vous au paragraphe Sélections d'entité partageables et non partageables ci-dessous.
Tous les attributs de stockage (texte, nombre, booléen, date) sont disponibles en tant que propriétés des sélections d'entités et des entités. Lorsqu'il est utilisé conjointement avec une sélection d'entité, un attribut scalaire renvoie une collection de valeurs scalaires. Par exemple :
locals:=ds.Person.query("city = :1";"San Jose") //Sélection des entités Person
localEmails:=locals.emailAddress //collection des adresses e-mail (chaînes)
Ce code renvoie dans localEmails une collection d'adresses e-mail sous forme de chaînes.
En plus de la variété de méthodes que vous pouvez utiliser, vous pouvez également utiliser les attributs relationnels comme propriétés des sélections d'entités pour renvoyer de nouvelles sélection d'entités. Par exemple, considérez la structure suivante :
myParts:=ds.Part.query("ID < 100") //Retourne Parts avec un ID inférieur à 100 $myInvoices:=myParts.invoiceItems.invoice //Toutes les factures ("Invoice") avec au moins une ligne ("InvoiceItem") reliée à un produit ("Part") dans myParts
La dernière ligne retournera dans $myInvoices une sélection d'entités de toutes les factures qui ont au moins un élément de factures lié à une partie de sélection d'entités dans myParts. Lorsqu'un attribut relationnel est utilisé en tant que propriété d'une sélection d'entités, le résultat est toujours une autre sélection d'entités, même si une seule entité est renvoyée. Lorsqu'un attribut relationnel est utilisé en tant que propriété d'une sélection d'entités et qu'aucune entité n'est renvoyée, le résultat est une sélection d'entités vide, et non null.
Une sélection d'entité peut être partageable (lisible par plusieurs process, mais non modifiable après création) ou altérable (prend en charge la fonction entitySelection.add( ), mais utilisable uniquement par le process courant) :
une sélection d'entités partageable présente les caractéristiques suivantes :
elle peut être stockée dans un objet ou une collection partagé(e), et peut être passée comme paramètre entre plusieurs process ou workers;
elle peut être stockée dans plusieurs objets ou collections partagé(e)s, ou dans un objet ou une collection partagé(e) qui appartient déjà à un groupe (sans identifiant de verrouillage).
elle ne permet pas d'ajouter de nouvelles entités. Essayer d'ajouter une entité à une sélection d'entité partageable génèrera une erreur (1637 - Cette sélection d'entité ne peut pas être modifiée). Pour ajouter une entité à une sélection d'entité partageable, vous devez d'abord la transformer en une sélection d'entité non partageable à l'aide de la fonction entitySelection.copy( ), avant d'appeler entitySelection.add( ).
Note : La plupart des fonctions de sélections d'entités (telles que entitySelection.slice( ), entitySelection.and( ), etc.) prennent en charge les sélections d'entités partageables car elles n'ont pas besoin de modifier la sélection d'entités d'origine (elles en retournent une nouvelle).
une sélection d'entité altérable présente les caractéristiques suivantes :
elle ne peut pas être partagée entre les process, ni être stockée dans un objet ou une collection partagé(e). Essayer de stocker une sélection d'entité non partageable dans un objet ou une collection partagé(e) génèrera une erreur (-10721 - Type de valeur non pris en charge dans un objet partagé ou une collection partagée)
elle accepte l'ajout de nouvelles entités, c'est-à-dire qu'elle prend en charge la fonction entitySelection.add( ).
La nature partageableou altérable d'une sélection d'entité est définie lors de la création de la sélection d'entité (elle ne peut pas être modifiée par la suite). Vous pouvez connaître la nature d'une sélection d'entité à l'aide de la fonction entitySelection.isAlterable( ) ou de la commande OB Is shared.
Une nouvelle sélection d'entité peut être partageable dans les cas suivants :
la nouvelle sélection d'entité est basée sur une relation entity.{attributeName} (par exemple company.employees) lorsque attributeName est un attribut lié à un-à-plusieurs et que l'entité n'appartient pas à une sélection d'entité.
la nouvelle sélection d'entité est explicitement copiée comme partageable avec entitySelection.copy( ) ou OB Copy (c'est-à-dire avec l'option ck shared).
Example:
$myComp:=ds.Company.get(2) //$myComp n'appartient pas à une sélection d'entité $employees:=$myComp.employees //$employees est partageable
Une nouvelle sélection d'entité est altérable dans les cas suivants :
the new entity selection is explicitely copied as aterable with entitySelection.copy( ) or OB Copy (i.e. without the ck shared option).
la nouvelle sélection d'entités est explicitement copiée comme étant altérable avec entitySelection.copy( ) ou OB Copy (c'est-à-dire sans l'option ck shared).
Exemple :
$toModify:=ds.Company.all().copy() //$toModify est altérable
Une nouvelle sélection d'entité hérite de la nature de la sélection d'entité d'origine dans les cas suivants :
la nouvelle sélection d'entité résulte de l'une des différentes fonctions de classe ORDA appliquées à une sélection d'entité existante (entitySelection.query( ), entitySelection.slice( ), etc.
la nouvelle sélection d'entité est basée sur une relation :
entity.{attributeName} (par exemple, company.employees) lorsque attributeName est un attribut relié 1 vers N et que l'entité appartient à une sélection d'entité (de même nature que la sélection d'entité entity.getSelection( ))
entitySelection.{nomAttribut} (par exemple, Employees.employer) lorsque attributeName est un attribut relié 1 vers N (de même nature que la sélection d'entité)
entitySelection.extract( ) lorsque la collection résultante contient des sélections d'entités (de même nature que la sélection d'entité)
Exemples :
$highSal:=ds.Employee.query("salary >= :1";1000000) //$highSal est partageable en raison de la requête sur dataClass $comp:=$highSal.employer //$comp est partageable car $highSal est partageable</p> <p> $lowSal:=ds.Employee.query("salary <= :1";10000).copy() //$lowSal est altérable en raison de copy() $comp2:=$lowSal.employer //$comp2 est altérable car $lowSal est altérable
Note de compatibilité : Il est possible de "forcer" toutes les nouvelles sélections d'entités à être altérables par défaut dans votre projet à l'aide de la méthode dataStore.makeSelectionsAlterable( ). Ce paramètre de compatibilité n'est pas recommandé pour les nouveaux projets.
Vous travaillez avec deux sélections d'entités que vous souhaitez passer à un process de worker pour que celui-ci puisse envoyer des e-mails aux destinataires appropriés :
var $paid;$unpaid : cs.InvoicesSelection //Nous obtenons des sélections d'entité pour les factures payées et impayées $paid:=ds.Invoices.query("status=:1";"Paid") $unpaid:=ds.Invoices.query("status=:1";"Unpaid")
//passer des références de sélections d'entité comme paramètres au worker CALL WORKER("mailing";"sendMails";$paid;$unpaid)
Méthode sendMails :
#DECLARE($paid cs.InvoicesSelection;$unpaid cs.InvoicesSelection)
var $invoice : cs.InvoicesEntity
var $server;$transporter;$email;$status : Object
//Preparer les e-mails $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"
//Boucle sur les sélections d'entité For each($invoice;$unpaid) $email.to:=$invoice.customer.address // adresse e-mail du client $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 // adresse e-mail du client $email.subject:="Please pay invoice # "+String($invoice.number) $status:=$transporter.send($email) End for each
Les sélections d'entités locales peuvent être triées ou non-triées. Fondamentalement, les deux objets ont des fonctionnalités similaires, mais il existe des différences en termes de performances et de fonctionnalités disponibles. Vous pouvez décider du type de sélection d'entités à utiliser en fonction de vos besoins spécifiques.
Note : Les sélections d'entités suivantes sont toujours triées:
sélections d'entités retournées par 4D Server vers un client distant
sélections d'entités fondées sur des datastores distants.
Les sélections d'entités non-ordonnées sont construites sur des bit tables en mémoire. Une sélection d'entités non-triées contient un bit pour chaque entité de la dataclass, indépendamment du fait que l'entité soit réellement ou pas dans la sélection. Chaque bit est égal à 1 ou 0, pour indiquer si l'entité est incluse ou non dans la sélection. C'est une représentation très compacte pour chaque entité. Par conséquent, les opérations utilisant des sélections d'entités non ordonnées sont très rapides. De plus, les sélections d'entités non ordonnées sont très économiques en terme d'espace mémoire. La taille d'une sélection d'entités non ordonnées, en octets, est toujours égale au nombre total d'entités de la dataclass divisé par 8. Par exemple, si vous créez une sélection d'entités non ordonnées pour une dataclass contenant 10 000 entités, elle occupera 1 250 octets, ce qui représente environ 1,2 Ko en RAM. D'un autre côté, les sélections d'entités non ordonnées ne peuvent pas être triées. Vous ne pouvez pas compter sur la position des entités dans la sélection. En outre, vous ne pouvez pas avoir plus d'une référence à la même entité dans la sélection : chaque entité ne peut être ajoutée qu'une seule fois.
Les sélections d'entités ordonnées sont construites sur des tableaux Entier long (contenant des références d'entités) en mémoire. Chaque référence à une entité prend 4 octets en mémoire. Le traitement et la maintenance de ces sélections prennent plus de temps et nécessitent plus d'espace mémoire que les sélections non-triées. D'un autre côté, elles peuvent être triées ou réorganisées, et vous pouvez compter sur des positions d'entités. En outre, vous pouvez ajouter plusieurs références à la même entité.
Le tableau suivant résume les principales caractéristiques de chaque type de sélection d'entités :
Fonctionnalité
Sélection d'entités non-triée
Sélection d'entités triée
Vitesse de traitement
très rapide
plus lent
Taille en mémoire
très petite
plus grande
Peut contenir plusieurs références à une entité
non
oui
Pour des raisons d'optimisation, par défaut, 4D ORDA crée généralement des sélections d'entités non-ordonnées, sauf lorsque vous utilisez la méthode orderBy( ) ou si vous utilisez les options appropriées (voir ci-dessous). Dans cette documentation, sauf indication contraire, "sélection d'entités" fait généralement réféernce à une "sélection d'entités non-ordonnée".
Comme mentionné ci-dessus, par défaut, ORDA crée et gère les sélections d'entités non-ordonnées à la suite d'opérations telles que des requêtes ou des comparaisons comme and( ). Les sélections d'entités ordonnées sont créées uniquement lorsque cela est nécessaire ou lorsqu'elles sont spécifiquement demandées à l'aide d'options.
Les sélections d'entités triées sont créées dans les cas suivants :
résultat d'un orderBy( ) sur une sélection (de n'importe quel type) ou un orderBy( ) sur une dataclass,
résultat de la méthode newSelection( ) avec l'option dk keep ordered.
Les sélections d'entités non-triées sont créées dans les cas suivants :
résultat d'un query( ) standard sur une sélection (de n'importe quel type) ou un query( ) sur une dataclass,
résultat de la méthode newSelection( ) sans option,
résultat de l'une des méthodes de comparaison, quel que soit le type de sélection saisi : or( ), and( ), minus( ).
Notez que lorsqu'une sélection d'entités ordonnée devient une sélection non-ordonnée, toute référence d'entité répétée est supprimée.
Si vous voulez transformer une sélection d'entités ordonnée en une sélection non-ordonnée, vous pouvez simplement lui appliquer une opération and( ), par exemple :
//mySel est une sélection d'entités ordonnée mySel:=mySel.and(mySel) //mySel est maintenant une sélection d'entités non-ordonnée
4D optimise automatiquement les requêtes ORDA qui utilisent des sélections d'entités ou qui chargent des entités en configuration client/serveur. Cette optimisation accélère l'exécution de votre application 4D en réduisant considérablement le volume d'informations transmises sur le réseau.
Les mécanismes d'optimisation suivants sont mis en oeuvre :
Lorsqu'un client demande une sélection d'entités au serveur, 4D "apprend" automatiquement les attributs de la sélection d'entités qui sont utilisés côté client lors de l'exécution du code et génère un "contexte d'optimisation". Ce contexte est relié à la sélection d'entités et stocke les attributs utilisés. Il sera mis à jour dynamiquement si d'autres attributs sont utilisés ultérieurement.
Les requêtes ultérieures qui sont envoyées au serveur à la même sélection d'entités réutilisent automatiquement le contexte d'optimisation et lisent uniquement les attributs nécessaires depuis le serveur, ce qui accélère le traitement. Par exemple, dans une listbox basée sur une sélection d'entités, la phase d'apprentissage a lieu durant l'affichage des premières lignes et l'affichage des lignes suivantes est fortement optimisé.
Un contexte d'optimisation existant peut être passé comme propriété à une autre sélection d'entités de la même dataclass, ce qui perment d'éviter la phase d'apprentissage et d'accélérer l'application (voir "Utilisation de la propriété context" ci-dessous).
Les méthodes suivantes associent automatiquement le contexte d'optimisation de la sélection d'entités d'origine à la sélection d'entités retournée :
$sel:=$ds.Employee.query("firstname = ab@") For each($e;$sel) $s:=$e.firstname+" "+$e.lastname+" works for "+$e.employer.name // $e.employer renvoie à la table Company End for each
Grâce à l'optimisation, cette requête récupérera uniquement les données issues des attributs utilisés (firstname, lastname, employer, employer.name) dans $sel après la phase d'apprentissage.
Utilisation de la propriété context
Vous pouvez tirer un meilleur parti de l'optimisation en utilisant la propriété context. Cette propriété référence un contexte d'optimisation "appris" pour une sélection d'entités. Elle peut être passée comme paramètre aux méthodes ORDA qui retournent de nouvelles sélections d'entités, afin que les sélections d'entités demandent directement au serveur les attributs utilisés, sans passer par la phase d'apprentissage.
Une même propriété de contexte d'optimisation peut être passée à un nombre illimité de sélections d'entités de la même dataclass. Toutes les méthodes ORDA qui gèrent les sélections d'entités prennent en charge la propriété context (par exemple les méthodes dataClass.query( ) ou dataClass.all( )). Il est toutefois important de garder à l'esprit qu'un contexte est automatiquement mis à jour lorsque de nouveaux attributs sont utilisés dans d'autres parties du code. Si le même contexte est réutilisé dans différents codes, il risque d'être surchargé et de perdre en efficacité.
Note : Un mécanisme similaire est mis en place pour des entités qui sont chargées, afin que seuls les attributs utilisés soient demandés (voir la méthode dataClass.get( )).
$sel1:=ds.Employee.query("lastname = S@";$querysettings) $data:=extractData($sel1) // Dans la méthode extractData, une optimisation est déclenchée et associée au contexte "shortList"
$sel2:=ds.Employee.query("lastname = Sm@";$querysettings) $data:=extractData($sel2) // Dans la méthode extractData, l'optimisation associée au contexte "shortList" est appliquée
$sel3:=ds.Employee.query("lastname = Smith";$querysettings2) $data:=extractDetailedData($sel3) // Dans la méthode extractDetailedData, une optimisation est déclenchée et associée au contexte "longList"
$sel4:=ds.Employee.query("lastname = Brown";$querysettings2) $data:=extractDetailedData($sel4) // Dans la méthode extractDetailedData, l'optimisation associée au contexte "longList" est appliquée
Listbox basée sur une sélection d'entités
L'optimisation d'une sélection d'entités s'applique automatiquement aux listbox basées sur une sélection d'entités dans les configurations client/serveur, au moment d'afficher et de dérouler le contenu d'une listbox : seuls les attributs affichés dans la listbox sont demandés depuis le serveur. Un contexte spécifique nommé "mode page" est également proposé lorsque l'entité courante de la sélection est chargée à l'aide de l'expression Élément courant de la listbox (voir List box type collection ou entity selection). Cette fonctionnalité vous permet de ne pas surcharger le contexte initial de la listbox dans ce cas précis, notamment si la "page" requiert des attributs supplémentaires. A noter que seule l'utilisation de l'expression Élément courant permettra de créer/utiliser le contexte de la page (l'accès via entitySelection[index] modifiera le contexte de la sélection d'entité). Cette optimisation sera également prise en charge par les requêtes ultérieures envoyées au serveur via les méthodes de navigation des entités. Les méthodes suivantes associeront automatiquement le contexte d'optimisation de l'entité source à l'entité retournée :
Par exemple, le code ci-dessous charge l'entité sélectionnée et permet de naviguer dans la sélection d'entités. Les entités sont chargées dans un contexte séparé et le contexte initial de la listbox demeure inchangé :
$myEntity:=Form.currentElement // expression de l'élément courant //... faire quelque chose $myEntity:=$myEntity.next() //charge la prochaine entité à l'aide du même contexte
Note de compatibilité : Les contextes gérés dans les connexions établies par Open datastore ne peuvent être utilisés qu'entre des versions main similaires de 4D. Par exemple, une application distante 4D v19.x ne peut utiliser que les contextes d'un datastore 4D Server v19.x.
Les propriétés des sélections d'entités sont toutefois énumérables :
ARRAY TEXT($prop;0) OB GET PROPERTY NAMES(ds.Employee.all();$prop) //$prop contient les noms des propriétés des sélections d'entités //("length", "00", "01"...)