Skip to main content
Version: 1.0.0

EntitySelection

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 created from existing selections using various functions of the DataClass class such as .all() or .query(), or functions of the EntityClass class itself, such as .and() or orderBy(). You can also create blank entity selections using the dataClass.newSelection() function.

Functions and properties

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

[index]

[index] : 4D.Entity

Description

The EntitySelection[index] notation allows you to access entities within the entity selection using the standard collection syntax: pass the position of the entity you want to get in the index parameter.

Note that the corresponding entity is reloaded from the datastore.

index can be any number between 0 and .length-1.

  • If index is out of range, an error is returned.
  • If index corresponds to a dropped entity, a Null value is returned.
caution

EntitySelection[index] is a non assignable expression, which means that it cannot be used as en editable entity reference with functions like .lock() or .save(). To work with the corresponding entity, you need to assign the returned expression to an assignable expression, such as a variable. Examples:

 sel = ds.Employee.all() //create the entity selection
//invalid statements:
result = sel[0].lock() //will NOT work
sel[0].lastName = "Smith" //will NOT work
result = sel[0].save() //will NOT work
//valid code:
entity = sel[0] //OK
entity.lastName = "Smith" //OK
entity.save() //OK

Example

 var employees : cs.EmployeeSelection
var employee : cs.EmployeeEntity
employees = ds.Employee.query("lastName = :1","H@")
employee = employees[2] // The 3rd entity of the employees entity selection is reloaded from the datastore

.at()

.at*( index : integer ) : 4D.Entity

ParameterTypeDescription
indexintegerIndex of entity to return
Result4D.EntityThe entity at that index

Description

The .at() function returns the entity at position index, allowing for positive and negative integer.

If index is negative (from -1 to -n with n : length of the entity selection), the returned entity will be based on the reverse order of the entity selection.

The function returns null if index is beyond entity selection limits.

Example

var employees : cs.EmployeeSelection
var emp1, emp2 : cs.EmployeeEntity
employees = ds.Employee.query("lastName = :1","H@")
emp1 = employees.at(2) //3rd entity of the employees entity selection
emp2 = employees.at(-3) //starting from the end, 3rd entity
//of the employees entity selection

.attributeName

.attributeName : collection
.attributeName : 4D.EntitySelection

Description

Any dataclass attribute can be used as a property of an entity selection to return a "projection" of values for the attribute in the entity selection. Projected values can be a collection or a new entity selection, depending on the kind (storage or relation) of the attribute.

  • If attributeName kind is storage: .attributeName returns a collection of values of the same type as attributeName.
  • If attributeName kind is relatedEntity: .attributeName returns a new entity selection of related values of the same type as attributeName. Duplications are removed (an unordered entity selection is returned).
  • If attributeName kind is relatedEntities: .attributeName returns a new entity selection of related values of the same type as attributeName. Duplications are removed (an unordered entity selection is returned).

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. In this case, if no entities are returned, the result is an empty entity selection.

If the attribute does not exist in the entity selection, an error is returned.

Example 1

Projection of storage values:

 var firstNames : collection
entitySelection = ds.Employee.all()
firstNames = entitySelection.firstName // firstName type is string

The resulting collection is a collection of strings, for example:

[
"Joanna",
"Alexandra",
"Rick"
]

Example 2

Projection of related entity:

 var es, entitySelection : cs.EmployeeSelection
entitySelection = ds.Employee.all()
es = entitySelection.employer // employer is related to a Company dataClass

The resulting object is an entity selection of Company with duplications removed (if any).

Example 3

Projection of related entities:

 var es : cs.EmployeeSelection
es = ds.Employee.all().directReports // directReports is related to Employee dataclass

The resulting object is an entity selection of Employee with duplications removed (if any).

.add()

.add( entity : 4D.Entity ) : 4D.EntitySelection

ParameterTypeDescription
entity4D.EntityEntity to be added to the entity selection
Result4D.EntitySelectionEntity selection including the added entity

Description

The .add() function adds the specified entity to the entity selection and returns the modified entity selection.

This function modifies the original entity selection.

caution

The entity selection must be alterable, i.e. it has been created for example by .newSelection(), otherwise .add() will return an error. Shareable entity selections do not accept the addition of entities. For more information, please refer to the Shareable or alterable entity selections section.

  • If the entity selection is ordered, entity is added at the end of the selection. If a reference to the same entity already belongs to the entity selection, it is duplicated and a new reference is added.
  • If the entity selection is unordered, entity is added anywhere in the selection, with no specific order.

The modified entity selection is returned by the function, so that function calls can be chained.

An error occurs if entity and the entity selection are not related to the same Dataclass. If entity is Null, no error is raised.

Example 1

 var employees : cs.EmployeeSelection
var employee : cs.EmployeeEntity
employees = ds.Employee.newSelection()
employee = ds.Employee.new()
employee.lastName = "Smith"
employee.save()
employees.add(employee) //The employee entity is added to the employees entity selection

Example 2

Calls to the function can be chained:

 var sel : cs.ProductSelection
var p1,p2,p3 : cs.ProductEntity

p1 = ds.Product.get(10)
p2 = ds.Product.get(11)
p3 = ds.Product.get(12)
sel = ds.Product.newSelection()
sel = sel.add(p1).add(p2).add(p3)

.and()

.and( entity : 4D.Entity ) : 4D.EntitySelection
.and( entitySelection : 4D.EntitySelection ) : 4D.EntitySelection

ParameterTypeDescription
entity4D.EntityEntity to intersect with
entitySelection4D.EntitySelectionEntity selection to intersect with
Result4D.EntitySelectionNew entity selection with the result of intersection with logical AND operator

Description

The .and() function combines the entity selection with an entity or entitySelection parameter using the logical AND operator; it returns a new, unordered entity selection that contains only the entities that are referenced in both the entity selection and the parameter.

  • If you pass entity as parameter, you combine this entity with the entity selection. If the entity belongs to the entity selection, a new entity selection containing only the entity is returned. Otherwise, an empty entity selection is returned.
  • If you pass entitySelection as parameter, you combine both entity selections. A new entity selection that contains only the entities that are referenced in both selections is returned. If there is no intersecting entity, an empty entity selection is returned.
note

You can compare ordered and/or unordered entity selections. The resulting selection is always unordered.

If the original entity selection or the entitySelection parameter is empty, or if the entity is Null, an empty entity selection is returned.

If the original entity selection and the parameter are not related to the same dataclass, an error is raised.

Example 1

 var employees, result : cs.EmployeeSelection
var employee : cs.EmployeeEntity
employees = ds.Employee.query("lastName = :1","H@")
//The employees entity selection contains the entity
//with primary key 710 and other entities
//for ex. "Colin Hetrick" / "Grady Harness" / "Sherlock Holmes" (primary key 710)
employee = ds.Employee.get(710) // Returns "Sherlock Holmes"

result = employees.and(employee) //result is an entity selection containing
//only the entity with primary key 710 ("Sherlock Holmes")

Example 2

We want to have a selection of employees named "Jones" who live in New York:

 var sel1, sel2, sel3 : cs.EmployeeSelection
sel1 = ds.Employee.query("name = :1","Jones")
sel2 = ds.Employee.query("city = :1","New York")
sel3 = sel1.and(sel2)

.average()

.average( attributePath : string ) : number

ParameterTypeDescription
attributePathstringAttribute path to be used for calculation
ResultnumberArithmetic mean (average) of entity attribute values (Undefined if empty entity selection)

Description

The .average() function returns the arithmetic mean (average) of all the non-null values of attributePath in the entity selection.

Pass in the attributePath parameter the attribute path to evaluate.

Only numerical values are taken into account for the calculation. Note however that, if the attributePath of the entity selection contains mixed value types, .average() takes all scalar elements into account to calculate the average value.

note

Date values are converted to numerical values (seconds) and used to calculate the average.

.average() returns undefined if the entity selection is empty or attributePath does not contain numerical values.

An error is returned if:

  • attributePath is a related attribute,
  • attributePath designates an attribute that does not exist in the entity selection dataclass.

Example

We want to obtain a list of employees whose salary is higher than the average salary:

 var averageSalary : number
var moreThanAv : cs.EmployeeSelection
averageSalary = ds.Employee.all().average("salary")
moreThanAv = ds.Employee.query("salary > :1",averageSalary)

.clean()

.clean*() : 4D.EntitySelection

ParameterTypeDescription
Result4D.EntitySelectionNew entity selection without deleted entities

Description

The .clean() function returns a new entity selection based upon the original entity selection but without deleted entities, if any.

By default, when an entity is dropped, its reference(s) in existing entity selection(s) become undefined but are not removed from the entity selection object(s). Deleted entities are still included in the .length property and are displayed as blank lines if the entity selection is bound to an interface object such as a list. In this case, calling the .clean() function on the entity selection allows you to get a new, up-to-date entity selection, not containing undefined entity references.

The resulting entity selection keeps the same order criteria and the same alterable/shareable property as the original entity selection.

Example

var sel , sel2 : cs.SpecialitySelection
var status : object

sel = ds.Speciality.query("ID <= 4")
status = ds.Speciality.get(2).drop() //delete the entity from the dataclass
//sel.length == 4

sel2 = sel.clean()
//sel2.length == 3

See also

.refresh()

.contains()

.contains( entity : 4D.Entity ) : boolean

ParameterTypeDescription
entity4D.EntityEntity to evaluate
ResultbooleanTrue if the entity belongs to the entity selection, else false

Description

The .contains() function returns true if entity reference belongs to the entity selection, and false otherwise.

In entity, specify the entity to search for in the entity selection. If entity is null, the function will return false.

If entity and the entity selection do not belong to the same dataclass, an error is raised.

Example

 var employees : cs.EmployeeSelection
var employee : cs.EmployeeEntity
var info : string

employees = ds.Employee.query("lastName = :1","H@")
employee = ds.Employee.get(610)

if(employees.contains(employee))
info = "The entity with primary key 610 has a last name beginning with H"
else
info = "The entity with primary key 610 does not have a last name beginning with H"
end

.count()

.count( attributePath : string ) : number

ParameterTypeDescription
attributePathstringPath of the attribute to be used for calculation
ResultnumberNumber of non null attributePath values in the entity selection

Description

The .count() function returns the number of entities in the entity selection with a non-null value in attributePath.

Only scalar values are taken into account. object or collection type values are considered as null values.

An error is returned if:

  • attributePath is a related attribute,
  • attributePath is not found in the entity selection dataclass.

Example

We want to find out the total number of employees for a company without counting any whose job title has not been specified:

 var sel : cs.EmployeeSelection
var count : number

sel = ds.Employee.query("employer = :1","Acme, Inc")
count = sel.count("jobtitle")

.copy()

.copy( { option : integer } ) : 4D.EntitySelection

ParameterTypeDescription
optionintegerkShared: return a shareable entity selection
Result4D.EntitySelectionCopy of the entity selection

Description

The .copy() function returns a copy of the original entity selection.

This function does not modify the original entity selection.

By default, if the option parameter is omitted, the function returns a new, alterable entity selection (even if the function is applied to a shareable entity selection). Pass the kShared constant in the option parameter if you want to create a shareable entity selection.

Example

You want to copy a regular entity selection to a shared entity selection in the storage:

localSel : cs.ProductSelection
localSel = ds.Products.newSelection() //create an empty selection
...
// The localSel entity selection is updated with another one
localSel.add(selectedProducts)

use(storage)
if(storage.products == null)
storage.products = newSharedObject()
end

use(storage.products)
storage.products = localSel.copy(kShared)
end
end

.distinct()

.distinct( attributePath : string { , option : integer } ) : collection

ParameterTypeDescription
attributePathstringPath of attribute whose distinct values you want to get
optionintegerkDiacritical, kCountValues
Resultcollectioncollection with only distinct values

Description

The .distinct() function returns a collection containing only distinct (different) values from the attributePath in the entity selection.

The returned collection is automatically sorted. Null values are not returned.

In the attributePath parameter, pass the entity attribute whose distinct values you want to get. Only scalar values (text, number, boolean, or date) can be handled. If the attributePath leads to an object property that contains values of different types, they are first grouped by type and sorted afterwards. Types are returned in the following order:

  1. booleans
  2. strings
  3. numbers
  4. dates

You can use the [] notation to designate a collection when attributePath is a path within an object (see examples).

In the options parameter, you can pass one or a combination of the following constants:

ConstantValueComment
kDiacritical8Evaluation is case sensitive and differentiates accented characters. By default if omitted, a non-diacritical evaluation is performed
kCountValues32Return the count of entities for every distinct value. When this option is passed, .distinct() returns a collection of objects containing a pair of {"value":*value*; "count":*count*} properties.
note

The kCountValues option is only available with storage attributes of type boolean, string, number, and date.

By default, a non-diacritical evaluation is performed. If you want the evaluation to be case sensitive or to differentiate accented characters, pass the kDiacritical constant in the option parameter.

An error is returned if:

  • attributePath is a related attribute,
  • attributePath is not found in the entity selection dataclass.

Examples

You want to get a collection containing a single element per country name:

 var countries : collection
countries = ds.Employee.all().distinct("address.country")

nicknames is a collection and extra is an object attribute:

values = ds.Employee.all().distinct("extra.nicknames[].first")

You want to get the number of different job names in the company:

var jobs : collection
jobs = ds.Employee.all().distinct("jobName",kCountValues)
//jobs[0] = {"value":"Developer","count":17}
//jobs[1] = {"value":"Office manager","count":5}
//jobs[2] = {"value":"Accountant","count":2}
//...

.distinctPaths()

.distinctPaths*( attribute : string ) : collection

ParameterTypeDescription
attributestringobject attribute name whose paths you want to get
ResultcollectionnewCollection with distinct paths

Description

The .distinctPaths() function returns a collection of distinct paths found in the indexed object attribute for the entity selection.

If attribute is not an indexed object attribute, an error is generated.

After the call, the size of the returned collection is equal to the number of distinct paths found in attribute for the entity selection. Paths are returned as strings including nested attributes and collections, for example "info.address.number" or "children[].birthdate". Entities with a null value in the attribute are not taken into account.

Example

You want to get all paths stored in a fullData object attribute:

var paths : collection
paths = ds.Employee.all().distinctPaths("fullData")
//paths[0] = "age"
//paths[1] = "Children"
//paths[2] = "Children[].age"
//paths[3] = "Children[].name"
//paths[4] = "Children.length"
///...
note

length is automatically added as path for nested collection properties.

.drop()

.drop( { mode : integer } ) : 4D.EntitySelection

ParameterTypeDescription
modeintegerkStopDroppingOnFirstError: stops method execution on first non-droppable entity
Result4D.EntitySelectionEmpty entity selection if successful, else entity selection containing non-droppable entity(ies)

Description

The .drop() function removes the entities belonging to the entity selection from the table related to its dataclass within the datastore. The entity selection remains in memory.

caution

Removing entities is permanent and cannot be undone. It is recommended to call this action in a transaction in order to have a rollback option.

If a locked entity is encountered during the execution of .drop(), it is not removed. By default, the function processes all entities of the entity selection and returns non-droppable entities in the entity selection. If you want the method to stop execution at the first encountered non-droppable entity, pass the kStopDroppingOnFirstError constant in the mode parameter.

Example

Example without the kStopDroppingOnFirstError option:

 var employees, notDropped : cs.EmployeeSelection
var info : string
employees = ds.Employee.query("firstName = :1","S@")
notDropped = employees.drop() // notDropped is an entity selection containing all the not dropped entities
if(notDropped.length == 0) //The delete action is successful, all the entities have been deleted
info = "You have dropped "+string(employees.length)+" employees" //The dropped entity selection remains in memory
else
info = "Problem during drop, try later"
end

Example with the kStopDroppingOnFirstError option:

 var employees, notDropped : cs.EmployeeSelection
var info : string
employees = ds.Employee.query("firstName = :1","S@")
notDropped = employees.drop(kStopDroppingOnFirstError) //notDropped is an entity selection containing the first not dropped entity
if(notDropped.length == 0) //The delete action is successful, all the entities have been deleted
info = "You have dropped "+string(employees.length)+" employees") //The dropped entity selection remains in memory
else
info = "Problem during drop, try later"
end

.extract()

.extract*( attributePath : string { , option : integer } ) : collection
.extract( attributePath { , targetPath } { , ...attributePathN : string , targetPathN : string } ) : collection

ParameterTypeDescription
attributePathstringAttribute path whose values must be extracted to the new collection
targetPathstringTarget attribute path or attribute name
optionintegerkKeepNull: include null attributes in the returned collection (ignored by default)
Resultcollectioncollection containing extracted values

Description

The .extract() function returns a collection containing attributePath values extracted from the entity selection.

attributePath can refer to:

  • a scalar dataclass attribute,
  • related entity,
  • related entities.

If attributePath is invalid, an empty collection is returned.

This function accepts two syntaxes.

.extract( attributePath : string { , option : integer } ) : collection

With this syntax, .extract() populates the returned collection with the attributePath values of the entity selection.

By default, entities for which attributePath is null or undefined are ignored in the resulting collection. You can pass the kKeepNull constant in the option parameter to include these values as null elements in the returned collection.

  • Dataclass attributes with .kind = "relatedEntity" are extracted as a collection of entities (duplications are kept).
  • Dataclass attributes with .kind = "relatedEntities" are extracted as a collection of entity selections.

.extract ( attributePath , targetPath { , ...attributePathN , ... targetPathN}) : collection

With this syntax, .extract() populates the returned collection with the attributePath properties. Each element of the returned collection is an object with targetPath properties filled with the corresponding attributePath properties. Null values are kept (option parameter is ignored with this syntax).

If several attributePath are given, a targetPath must be given for each. Only valid pairs [attributePath, targetPath] are extracted.

  • Dataclass attributes with .kind = "relatedEntity" are extracted as an entity.
  • Dataclass attributes with .kind = "relatedEntities" are extracted as an entity selection.
note

Entities of a collection of entities accessed by [] are not reloaded from the datastore.

Example

Given the following table and relation:

 var firstnames, addresses, mailing, teachers : collection
//
//
//firstnames is a collection of Strings


firstnames = ds.Teachers.all().extract("firstname")
//
//addresses is a collection of entities related to dataclass Address
//Null values for address are extracted
addresses = ds.Teachers.all().extract("address",kKeepNull)
//
//
//mailing is a collection of objects with properties "who" and "to"
//"who" property content is string type
//"to" property content is entity type (Address dataclass)
mailing = ds.Teachers.all().extract("lastname","who","address","to")
//
//
//mailing is a collection of objects with properties "who" and "city"
//"who" property content is string type
//"city" property content is string type
mailing = ds.Teachers.all().extract("lastname","who","address.city","city")
//
//teachers is a collection of objects with properties "where" and "who"
//"where" property content is string
//"who" property content is an entity selection (Teachers dataclass)
teachers = ds.Address.all().extract("city","where","teachers","who")
//
//teachers is a collection of entity selections
teachers = ds.Address.all().extract("teachers")

.first()

.first() : 4D.Entity

ParameterTypeDescription
Result4D.EntityReference to the first entity of the entity selection (Null if selection is empty)

Description

The .first() function returns a reference to the entity in the first position of the entity selection.

The result of this function is similar to:

 entity = entitySel[0]

There is, however, a difference between both statements when the selection is empty:

 var entitySel : cs.EmpSelection
var entity : cs.EmpEntity
entitySel = ds.Emp.query("lastName = :1","Nonexistentname") //no matching entity
//entity selection is then empty
entity = entitySel.first() //returns Null
entity = entitySel[0] //generates an error

Example

 var entitySelection : cs.EmpSelection
var entity : cs.EmpEntity
entitySelection = ds.Emp.query("salary > :1",100000)
if(entitySelection.length != 0)
entity = entitySelection.first()
end

.getDataClass()

.getDataClass() : 4D.DataClass

ParameterTypeDescription
Result4D.DataClassDataclass object to which the entity selection belongs

Description

The .getDataClass() function returns the dataclass of the entity selection.

This function is mainly useful in the context of generic code.

Example

The following generic code duplicates all entities of the entity selection:

  //duplicate_entities method
//duplicate_entities(entity_selection)

declare ( entitySelection : 4D.EntitySelection )
var dataClass : 4D.DataClass
var entity, duplicate : 4D.Entity
var status : object
dataClass = entitySelection.getDataClass()
forEach(entity,entitySelection)
duplicate = dataClass.new()
duplicate.fromObject(entity.toObject())
duplicate[dataClass.getInfo().primaryKey] = null //reset the primary key
status = duplicate.save()
end

.isAlterable()

.isAlterable() : boolean

ParameterTypeDescription
ResultbooleanTrue if the entity selection is alterable, False otherwise

Description

The .isAlterable() function returns true if the entity selection is alterable, and false if the entity selection is not alterable.

For more information, please refer to the Shareable or alterable entity selections section.

.isOrdered()

.isOrdered() : boolean

ParameterTypeDescription
Resultbooleantrue if the entity selection is ordered, false otherwise

Description

The .isOrdered() function returns true if the entity selection is ordered, and false if it is unordered.

For more information, please refer to the Ordered or unordered entity selection section.

Example

 var employees : cs.EmployeeSelection
var employee : cs.EmployeeEntity
var isOrdered : boolean
var info : string
employees = ds.Employee.newSelection(kKeepOrdered)
employee = ds.Employee.get(714) // Gets the entity with primary key 714

//In an ordered entity selection, we can add the same entity several times (duplications are kept)
employees.add(employee)
employees.add(employee)
employees.add(employee)

isOrdered = employees.isOrdered()
if(isOrdered)
info = "The entity selection is ordered and contains "+string(employees.length)+" employees"
end

.last()

.last() : 4D.Entity

ParameterTypeDescription
Result4D.EntityReference to the last entity of the entity selection (null if empty entity selection)

Description

The .last() function returns a reference to the entity in last position of the entity selection.

The result of this function is similar to:

 entity = entitySel[length-1]

If the entity selection is empty, the function returns null.

Example

 var entitySelection : cs.EmpSelection
var entity : cs.EmpEntity
entitySelection = ds.Emp.query("salary < :1",50000)
if(entitySelection.length != 0)
entity = entitySelection.last()
end

.length

.length : integer

Description

The .length property returns the number of entities in the entity selection. If the entity selection is empty, it returns 0.

Entity selections always have a .length property.

Example

 var vSize : integer
var info : string
vSize = ds.Employee.query("gender = :1","male").length
info = string(vSize)+" male employees found."

.max()

.max( attributePath : string ) : any

ParameterTypeDescription
attributePathstringPath of the attribute to be used for calculation
ResultanyHighest value of attribute

Description

The .max() function returns the highest (or maximum) value among all the values of attributePath in the entity selection. It actually returns the value of the last entity of the entity selection as it would be sorted in ascending order using the .orderBy() function.

If you pass in attributePath a path to an object property containing different types of values, the .max() function will return the maximum value of the first scalar type found in the default type list order:

  1. booleans
  2. strings
  3. numbers
  4. objects
  5. collections
  6. dates

.max() returns undefined if the entity selection is empty or attributePath is not found in the object attribute.

An error is returned if:

  • attributePath is a related attribute,
  • attributePath designates an attribute that does not exist in the entity selection dataclass.

Example

We want to find the highest salary among all the female employees:

 var sel : cs.EmpSelection
var maxSalary : number
sel = ds.Employee.query("gender = :1","female")
maxSalary = sel.max("salary")

.min()

.min( attributePath : string ) : any

ParameterTypeDescription
attributePathstringPath of the attribute to be used for calculation
ResultanyLowest value of attribute

Description

The .min() function returns the lowest (or minimum) value among all the values of attributePath in the entity selection. It actually returns the first entity of the entity selection as it would be sorted in ascending order using the .orderBy() function (excluding null values).

If you pass in attributePath a path to an object property containing different types of values, the .min() function will return the minimum value within the first scalar value type found in the default type list order (see .max() description).

.min() returns undefined if the entity selection is empty or attributePath is not found in the object attribute.

An error is returned if:

  • attributePath is a related attribute,
  • attributePath designates an attribute that does not exist in the entity selection dataclass.

Example

In this example, we want to find the lowest salary among all the female employees:

 var sel : cs.EmpSelection
var minSalary : number
sel = ds.Employee.query("gender = :1","female")
minSalary: = sel.min("salary")

.minus()

.minus( entity : 4D.Entity ) : 4D.EntitySelection
.minus( entitySelection : 4D.EntitySelection ) : 4D.EntitySelection

ParameterTypeDescription
entity4D.EntityEntity to substract
entitySelection4D.EntitySelectionEntity selection to substract
Result4D.EntitySelectionNew entity selection or a new reference on the existing entity selection

Description

The .minus() function excludes from the entity selection to which it is applied the entity or the entities of entitySelection and returns the resulting entity selection.

  • If you pass entity as parameter, the function creates a new entity selection without entity (if entity belongs to the entity selection). If entity was not included in the original entity selection, a new reference to the entity selection is returned.
  • If you pass entitySelection as parameter, the function returns an entity selection containing the entities belonging to the original entity selection without the entities belonging to entitySelection.

You can compare ordered and/or unordered entity selections. The resulting selection is always unordered.

If the original entity selection or both the original entity selection and the entitySelection parameter are empty, an empty entity selection is returned.

If entitySelection is empty or if entity is null, a new reference to the original entity selection is returned.

If the original entity selection and the parameter are not related to the same dataclass, an error is raised.

Example 1

 var employees, result : cs.EmployeeSelection
var employee : cs.EmployeeEntity

employees = ds.Employee.query("lastName = :1","H@")
// The employees entity selection contains the entity with primary key 710 and other entities
// for ex. "Colin Hetrick", "Grady Harness", "Sherlock Holmes" (primary key 710)

employee = ds.Employee.get(710) // Returns "Sherlock Holmes"

result = employees.minus(employee) //result contains "Colin Hetrick", "Grady Harness"

Example 2

We want to have a selection of female employees named "Jones" who live in New York :

 var sel1, sel2, sel3 : cs.EmployeeSelection
sel1 = ds.Employee.query("name = :1","Jones")
sel2 = ds.Employee.query("city = :1","New York")
sel3 = sel1.and(sel2).minus(ds.Employee.query("gender = 'male'"))

.or()

.or( entity : 4D.Entity ) : 4D.EntitySelection
.or( entitySelection : 4D.EntitySelection ) : 4D.EntitySelection

ParameterTypeDescription
entity4D.EntityEntity to intersect with
entitySelection4D.EntitySelectionEntity selection to intersect with
Result4D.EntitySelectionNew entity selection or new reference to the original entity selection

Description

The .or() function combines the entity selection with the entity or entitySelection parameter using the logical (not exclusive) OR operator; it returns a new, unordered entity selection that contains all the entities from the entity selection and the parameter.

  • If you pass entity as parameter, you compare this entity with the entity selection. If the entity belongs to the entity selection, a new reference to the entity selection is returned. Otherwise, a new entity selection containing the original entity selection and the entity is returned.
  • If you pass entitySelection as parameter, you compare entity selections. A new entity selection containing the entities belonging to the original entity selection or entitySelection is returned (or is not exclusive, entities referenced in both selections are not duplicated in the resulting selection).

You can compare ordered and/or unordered entity selections. The resulting selection is always unordered.

If the original entity selection and the entitySelection parameter are empty, an empty entity selection is returned. If the original entity selection is empty, a reference to entitySelection or an entity selection containing only entity is returned.

If entitySelection is empty or if entity is Null, a new reference to the original entity selection is returned.

If the original entity selection and the parameter are not related to the same dataclass, an error is raised.

Example 1

 var employees1, employees2, result : cs.EmployeeSelection
employees1 = ds.Employee.query("lastName = :1","H@") //Returns "Colin Hetrick","Grady Harness"
employees2 = ds.Employee.query("firstName = :1","C@") //Returns "Colin Hetrick", "Cath Kidston"
result = employees1.or(employees2) //result contains "Colin Hetrick", "Grady Harness","Cath Kidston"

Example 2

 var employees, result : cs.EmployeeSelection
var employee : cs.EmployeeEntity
employees = ds.Employee.query("lastName = :1","H@") // Returns "Colin Hetrick","Grady Harness", "Sherlock Holmes"
employee = ds.Employee.get(686) //the entity with primary key 686 does not belong to the $employees entity selection
//It matches the employee "Mary Smith"

result = employees.or(employee) //result contains "Colin Hetrick", "Grady Harness", "Sherlock Holmes", "Mary Smith"

.orderBy()

.orderBy( pathString : string ) : 4D.EntitySelection
.orderBy( pathObjects : collection ) : 4D.EntitySelection

ParameterTypeDescription
pathStringstringAttribute path(s) and sorting instruction(s) for the entity selection
pathObjectscollectioncollection of criteria objects
Result4D.EntitySelectionNew entity selection in the specified order

Description

The .orderBy() function returns a new ordered entity selection containing all entities of the entity selection in the order specified by pathString or pathObjects criteria.

This function does not modify the original entity selection.

You must use a criteria parameter to define how the entities must be sorted. Two different parameters are supported:

  • pathString (string): this parameter contains a formula made of 1 to X attribute paths and (optionally) sort orders, separated by commas. The syntax is:
"attributePath1 {desc or asc}, attributePath2 {desc or asc},..."

The order in which the attributes are passed determines the sorting priority of the entities. By default, attributes are sorted in ascending order. You can set the sort order of a property in the criteria string, separated from the property path by a single space: pass "asc" to sort in ascending order or "desc" in descending order.

  • pathObjects (collection): each element of the collection contains an object structured in the following way:
{
"propertyPath": string,
"descending": boolean
}

By default, attributes are sorted in ascending order ("descending" is false).

You can add as many objects in the criteria collection as necessary.

Null values are evaluated as less than other values.

Example

// order by formula
sortedEntitySelection = entitySelection.orderBy("firstName asc, salary desc")
sortedEntitySelection = entitySelection.orderBy("firstName")

// order by collection with or without sort orders
orderColl = newCollection
orderColl.push(newObject("propertyPath","firstName","descending",false))
orderColl.push(newObject("propertyPath","salary","descending",true))
sortedEntitySelection = entitySelection.orderBy(orderColl)

orderColl = newCollection
orderColl.push(newObject("propertyPath","manager.lastName"))
orderColl.push(newObject("propertyPath","salary"))
sortedEntitySelection = entitySelection.orderBy(orderColl)

.orderByFormula( )

.orderByFormula( formulaString : string { , sortOrder : integer } { , settings : object} ) : 4D.EntitySelection
.orderByFormula( formulaObj : object { , sortOrder : integer } { , settings : object} ) : 4D.EntitySelection

ParameterTypeDescription
formulaStringstringformula string
formulaObj4D.Formulaformula object
sortOrderintegerkAscending (default) or kDescending
settingsobjectParameter(s) for the formula
Result4D.EntitySelectionNew ordered entity selection

Description

The .orderByFormula() function returns a new, ordered entity selection containing all entities of the entity selection in the order defined through the formulaString or formulaObj and, optionally, sortOrder and settings parameters.

This function does not modify the original entity selection.

You can use either a formulaString or a formulaObj parameter:

  • formulaString: you pass a QodlyScript expression such as "Year of(this.birthDate)".
  • formulaObj: pass a valid formula object created using the formula or formula from string command.

The formulaString or formulaObj is executed for each entity of the entity selection and its result is used to define the position of the entity in the returned entity selection. The result must be of a sortable type (boolean, date, number, text, time, null).

A null result is always the smallest value.

By default if you omit the sortOrder parameter, the resulting entity selection is sorted in ascending order. Optionnally, you can pass one of the following values in the sortOrder parameter:

ConstantValueComment
kAscending0Ascending sort order (default)
kDescending1Descending sort order

Within the formulaString or formulaObj, the processed entity and thus its attributes are available through the This command (for example, this.lastName).

You can pass parameter(s) to the formula using the args property (object) of the settings parameter: the formula receives the settings.args object in a special variable named $1.

Example 1

Sorting students using a formula provided as text:

 var es1, es2 : cs.StudentsSelection
es1 = ds.Students.query("nationality = :1","French")
es2 = es1.orderByFormula("length(this.lastname)") //ascending by default
es2 = es1.orderByFormula("length(this.lastname)",kDescending)

Same sort order but using a formula object:

 var es1, es2 : cs.StudentsSelection
var vFormula : 4D.Function
es1 = ds.Students.query("nationality = :1","French")
vFormula = formula(length(this.lastname))
es2 = es1.orderByFormula(vFormula) // ascending by default
es2 = es1.orderByFormula(vFormula,kDescending)

Example 2

A formula is given as a formula object with parameters; settings.args object is received in the $1 variable in the computeAverage method.

In this example, the "marks" object field in the Students dataClass contains students' grades for each subject. A single formula object is used to compute a student's average grade with different coefficients for schoolA and schoolB.

 var es1, es2 : cs.StudentsSelection
var vFormula, schoolA, schoolB : object
es1 = ds.Students.query("nationality = :1","French")
vFormula = formula(computeAverage($1))

schoolA = newObject() //settings object
schoolA.args = newObject("english",1,"math",1,"history",1) // Coefficients to compute an average

//Order students according to school A criteria
es2 = es1.entitySelection.orderByFormula(vFormula,schoolA)

schoolB = newObject() //settings object
schoolB.args = newObject("english",1,"math",2,"history",3) // Coefficients to compute an average

//Order students according to school B criteria
es2 = es1.entitySelection.orderByFormula(vFormula,kDescending,schoolB)
  //
// computeAverage method
// -----------------------------
declare (coefList : object) -> result : integer
var subject : string
var vAverage, vSum : integer

vAverage = 0
vSum = 0

forEach(subject,coefList)
vSum = vSum+coefList[subject]
end

forEach(subject,this.marks)
vAverage = vAverage+(this.marks[subject]*coefList[subject])
end

result = vAverage/vSum

.query()

.query( queryString : string { , ...value : any } { , querySettings : object } ) : 4D.EntitySelection
.query( formula : object { , querySettings : object } ) : 4D.EntitySelection

ParameterTypeDescription
queryStringstringSearch criteria as string
formula4D.FormulaSearch criteria as formula object
valueanyValue(s) to use for indexed placeholder(s)
querySettingsobjectQuery options: parameters, attributes, args, allowFormulas, context, queryPath, queryPlan
Result4D.EntitySelectionNew entity selection made up of entities from entity selection meeting the search criteria specified in queryString or formula

Description

The .query() function searches for entities that meet the search criteria specified in queryString or formula and (optionally) value(s) among all the entities in the entity selection, and returns a new object of type EntitySelection containing all the entities that are found. Lazy loading is applied.

This function does not modify the original entity selection.

If no matching entities are found, an empty EntitySelection is returned.

For detailed information on how to build a query using queryString, value, and querySettings parameters, please refer to the DataClass .query() function description.

By default if you omit the order by statement in the queryString, the returned entity selection is not ordered.

Example 1

 var entitySelectionTemp, myEntitySel : cs.EmployeeSelection
entitySelectionTemp = ds.Employee.query("lastName = :1","M@")
myEntitySel = entitySelectionTemp.query("manager.lastName = :1","S@")

Example 2

More examples of queries can be found in the DataClass .query() page.

See also

.query() for dataclass

.queryPath

.queryPath : string

Description

The .queryPath property contains a detailed description of the query as it was actually performed by Qodly. This property is available for EntitySelection objects generated through queries if the "queryPath":true property was passed in the querySettings parameter of the .query() function.

For more information, refer to the querySettings parameter paragraph in the Dataclass.query() page.

.queryPlan

.queryPlan : string

Description

The .queryPlan property contains a detailed description of the query just before it is executed (i.e., the planned query). This property is available for EntitySelection objects generated through queries if the "queryPlan":true property was passed in the querySettings parameter of the .query() function.

For more information, refer to the querySettings parameter paragraph in the Dataclass.query() page.

.selected()

.selected( selectedEntities : 4D.EntitySelection ) : object

ParameterTypeDescription
selectedEntities4D.EntitySelectionEntity selection with entities for which to know the rank in the entity selection
ResultobjectRange(s) of selected entities in entity selection

Description

The .selected() function returns an object describing the position(s) of selectedEntities in the original entity selection.

This function does not modify the original entity selection.

Pass in the selectedEntities parameter an entity selection containing entities for which you want to know the position in the original entity selection. selectedEntities must be an entity selection belonging to the same dataclass as the original entity selection, otherwise an error 1587 - "The entity selection comes from an incompatible dataclass" is raised.

Result

The returned object contains the following properties:

PropertyTypeDescription
rangescollectioncollection of range objects
ranges[].startintegerFirst entity index in the range
ranges[].endintegerLast entity index in the range

If a ranges property contains a single entity, start = end`. Index starts at 0.

The function returns an empty collection in the ranges property if the original entity selection or the selectedEntities entity selection is empty.

Example

var invoices, cashSel, creditSel : cs.InvoicesSelection
var result1, result2 : object

invoices = ds.Invoices.all()

cashSel = ds.Invoices.query("payment = :1", "Cash")
creditSel = ds.Invoices.query("payment IN :1", newCollection("Cash", "Credit Card"))

result1 = invoices.selected(cashSel)
result2 = invoices.selected(creditSel)

//result1 = {ranges:[{start:0;end:0},{start:3;end:3},{start:6;end:6}]}
//result2 = {ranges:[{start:0;end:1},{start:3;end:4},{start:6;end:7}]}

.slice()

.slice( startFrom : integer { , end : integer } ) : 4D.EntitySelection

ParameterTypeDescription
startFromintegerIndex to start the operation at (included)
endintegerEnd index (not included)
Result4D.EntitySelectionNew entity selection containing sliced entities (shallow copy)

Description

The .slice() function returns a portion of an entity selection into a new entity selection, selected from the startFrom index to the end index (end is not included) or to the last entity of the entity selection. This method returns a shallow copy of the entity selection (it uses the same entity references).

This function does not modify the original entity selection.

The returned entity selection contains the entities specified by startFrom and all subsequent entities up to, but not including, the entity specified by end. If only the startFrom parameter is specified, the returned entity selection contains all entities from startFrom to the last entity of the original entity selection.

  • If startFrom < 0, it is recalculated as startFrom: = startFrom+length (it is considered as the offset from the end of the entity selection). If the calculated value < 0, startFrom is set to 0.
  • If startFrom >= length, the function returns an empty entity selection.
  • If end < 0, it is recalculated as end: = end+length.
  • If end < startFrom (passed or calculated values), the method does nothing.

If the entity selection contains entities that were dropped in the meantime, they are also returned.

Example 1

You want to get a selection of the first 9 entities of the entity selection:

var sel, sliced : cs.EmployeeSelection
sel = ds.Employee.query("salary > :1",50000)
sliced = sel.slice(0,9) //

Example 2

Assuming we have ds.Employee.all().length = 10

var slice : cs.EmployeeSelection
slice = ds.Employee.all().slice(-1,-2) //tries to return entities from index 9 to 8,
//but since 9 > 8, returns an empty entity selection

.sum()

.sum( attributePath : string ) : number

ParameterTypeDescription
attributePathstringPath of the attribute to be used for calculation
ResultnumberSum of entity selection values

Description

The .sum() function returns the sum for all attributePath values in the entity selection.

.sum() returns 0 if the entity selection is empty.

The sum can only be done on values of number type. If the attributePath is an object property, only numerical values are taken into account for the calculation (other value types are ignored). In this case, if attributePath leads to a property that does not exist in the object or does not contain any numeric values, .sum() returns 0.

An error is returned if:

  • attributePath is not a numerical or an object attribute,
  • attributePath is a related attribute,
  • attributePath is not found in the entity selection dataclass.

Example

var sel : cs.EmployeeSelection
var sum : number

sel = ds.Employee.query("salary < :1",20000)
sum = sel.sum("salary")

.toCollection( )

.toCollection( { options : integer { , begin : integer { , howMany : integer } } ) : collection
.toCollection( filterString : string {, options : integer { , begin : integer { , howMany : integer }}} ) : collection
.toCollection( filterCol : collection {, options : integer { , begin : integer { , howMany : integer }}} ) : collection

ParameterTypeDescription
filterStringstringstring with entity attribute path(s) to extract
filterColcollectioncollection of entity attribute path(s) to extract
optionsintegerkWithPrimaryKey: adds the primary key
kWithStamp: adds the stamp
beginintegerDesignates the starting index
howManyintegerNumber of entities to extract
Resultcollectioncollection of objects containing attributes and values of entity selection

Description

The .toCollection() function creates and returns a collection where each element is an object containing a set of properties and values corresponding to the attribute names and values for the entity selection.

If no filter parameter is passed or the first parameter contains an empty string or *, all the attributes are extracted. Attributes with kind property as "relatedEntity" are extracted with the simple form: an object with property \_\_KEY (primary key). Attributes with kind property as "relatedEntities" are not extracted.

Or, you can designate the entity attributes to extract using a filter parameter. You can use one of these two filters:

  • filterString --a string with property paths separated with commas: "propertyPath1, propertyPath2, ...".
  • filterCol --a collection of strings containing property paths: ["propertyPath1","propertyPath2",...]

If a filter is specified for an attribute of the relatedEntity kind:

  • propertyPath = "relatedEntity" -> it is extracted with simple form
  • propertyPath = "relatedEntity.*" -> all the properties are extracted
  • propertyPath = "relatedEntity.propertyName1, relatedEntity.propertyName2, ..." -> only those properties are extracted

If a filter is specified for an attribute of the relatedEntities kind:

  • propertyPath = "relatedEntities.*" -> all the properties are extracted
  • propertyPath = "relatedEntities.propertyName1, relatedEntities.propertyName2, ..." -> only those properties are extracted

In the options parameter, you can pass the kWithPrimaryKey and/or kWithStamp selector(s) to add the entity's primary keys and/or stamps in extracted objects.

The begin parameter allows you to indicate the starting index of the entities to extract. You can pass any value between 0 and entity selection length-1.

The howMany parameter lets you specify the number of entities to extract, starting with the one specified in begin. Dropped entities are not returned but are taken into account according to howMany. For example, if howMany = 3 and there is 1 dropped entity, only 2 entities are extracted.

If howMany > length of the entity selection, the method returns (length - begin) objects.

An empty collection is returned if:

  • the entity selection is empty, or
  • begin is greater than the length of the entity selection.

Example 1

The following structure will be used throughout all examples of this section:

Example without filter or options parameter:

 var employeesCollection : collection
var employees : cs.EmployeeSelection

employeesCollection = newCollection
employees = ds.Employee.all()
employeesCollection = employees.toCollection()

Returns:

[
{
"ID": 416,
"firstName": "Gregg",
"lastName": "Wahl",
"salary": 79100,
"birthDate": "1963-02-01T00:00:00.000Z",
"woman": false,
"managerID": 412,
"employerID": 20,
"photo": "[object Picture]",
"extra": null,
"employer": {
"__KEY": 20
},
"manager": {
"__KEY": 412

}
},
{
"ID": 417,
"firstName": "Irma",
"lastName": "Durham",
"salary": 47000,
"birthDate": "1992-06-16T00:00:00.000Z",
"woman": true,
"managerID": 412,
"employerID": 20,
"photo": "[object Picture]",
"extra": null,
"employer": {
"__KEY": 20
},
"manager": {
"__KEY": 412
}
}
]

Example 2

Example with options:

var employeesCollection : collection
var employees : cs.EmployeeSelection

employeesCollection = newCollection
employees = ds.Employee.all()
employeesCollection = employees.toCollection("",kWithPrimaryKey+kWithStamp)

Returns:

[
{
"__KEY": 416,
"__STAMP": 1,
"ID": 416,
"firstName": "Gregg",
"lastName": "Wahl",
"salary": 79100,
"birthDate": "1963-02-01T00:00:00.000Z",
"woman": false,
"directReports": 412,
"employer": 20,
"photo": "[object Picture]",
"extra": null,
"employer": {
"__KEY": 20
},
"manager": {
"__KEY": 412
}
},
{
"__KEY": 417,
"__STAMP": 1,
"ID": 417,
"firstName": "Irma",
"lastName": "Durham",
"salary": 47000,
"birthDate": "1992-06-16T00:00:00.000Z",
"woman": true,
"directReports": 412,
"employerID": 20,
"photo": "[object Picture]",
"extra": null,
"employer": {
"__KEY": 20
},
"manager": {
"__KEY": 412
}
}]

Example 3

Example with slicing and filtering on properties:

var employeesCollection, filter : collection
var employees : cs.EmployeeSelection

employeesCollection = newCollection
filter = newCollection
filter.push("firstName")
filter.push("lastName")

employees = ds.Employee.all()
employeesCollection = employees.toCollection(filter,0,0,2)

Returns:

[
{
"firstName": "Gregg",
"lastName": "Wahl"
},
{
"firstName": "Irma",
"lastName": "Durham"
}
]

Example 4

Example with relatedEntity type with simple form:

var employeesCollection : collection
employeesCollection = newCollection
employeesCollection = employees.toCollection("firstName,lastName,employer")

returns:

[
{
"firstName": "Gregg",
"lastName": "Wahl",
"employer": {
"__KEY": 20
}
},
{
"firstName": "Irma",
"lastName": "Durham",
"employer": {
"__KEY": 20
}
},
{
"firstName": "Lorena",
"lastName": "Boothe",
"employer": {
"__KEY": 20
}
}
]

Example 5

Example with filterCol parameter:

var employeesCollection, coll : collection
employeesCollection = newCollection
coll = newCollection("firstName","lastName")
employeesCollection = employees.toCollection(coll)

Returns:

[
{
"firstName": "Joanna",
"lastName": "Cabrera"
},
{
"firstName": "Alexandra",
"lastName": "Coleman"
}
]

Example 6

Example with extraction of all properties of a relatedEntity:

var employeesCollection, coll : collection
employeesCollection = newCollection
coll = newCollection
coll.push("firstName")
coll.push("lastName")
coll.push("employer.*")
employeesCollection = employees.toCollection(coll)

Returns:

[
{
"firstName": "Gregg",
"lastName": "Wahl",
"employer": {
"ID": 20,
"name": "India Astral Secretary",
"creationDate": "1984-08-25T00:00:00.000Z",
"revenues": 12000000,
"extra": null
}
},
{
"firstName": "Irma",
"lastName": "Durham",
"employer": {
"ID": 20,
"name": "India Astral Secretary",
"creationDate": "1984-08-25T00:00:00.000Z",
"revenues": 12000000,
"extra": null
}
},
{
"firstName": "Lorena",
"lastName": "Boothe",
"employer": {
"ID": 20,
"name": "India Astral Secretary",
"creationDate": "1984-08-25T00:00:00.000Z",
"revenues": 12000000,
"extra": null
}
}
]

Example 7

Example with extraction of some properties of a relatedEntity:

var employeesCollection : collection
employeesCollection = newCollection
employeesCollection = employees.toCollection("firstName, lastName, employer.name")
[
{
"firstName": "Gregg",
"lastName": "Wahl",

"employer": {
"name": "India Astral Secretary"
}
},
{
"firstName": "Irma",
"lastName": "Durham",
"employer": {
"name": "India Astral Secretary"
}
},
{
"firstName": "Lorena",
"lastName": "Boothe",
"employer": {
"name": "India Astral Secretary"
}
}]

Example 8

Example with extraction of some properties of relatedEntities:

 var employeesCollection : collection
employeesCollection = newCollection
employeesCollection = employees.toCollection("firstName, lastName, directReports.firstName")

Returns:

[
{
"firstName": "Gregg",
"lastName": "Wahl",
"directReports": []
},
{
"firstName": "Mike",
"lastName": "Phan",
"directReports": [
{
"firstName": "Gary"
},
{
"firstName": "Sadie"
},
{
"firstName": "Christie"
}
]
},
{
"firstName": "Gary",

"lastName": "Reichert",
"directReports": [
{
"firstName": "Rex"
},
{
"firstName": "Jenny"
},
{
"firstName": "Lowell"
}
]
}]

Example 9

Example with extraction of all properties of relatedEntities:

var employeesCollection : collection
employeesCollection = newCollection
employeesCollection = employees.toCollection("firstName, lastName, directReports.*")

[
{
"firstName": "Gregg",
"lastName": "Wahl",
"directReports": []
},
{
"firstName": "Mike",
"lastName": "Phan",
"directReports": [
{
"ID": 425,
"firstName": "Gary",
"lastName": "Reichert",
"salary": 65800,
"birthDate": "1957-12-23T00:00:00.000Z",
"woman": false,
"managerID": 424,
"employerID": 21,
"photo": "[object Picture]",
"extra": null,
"employer": {
"__KEY": 21
},
"manager": {
"__KEY": 424
}
},
{
"ID": 426,
"firstName": "Sadie",
"lastName": "Gallant",
"salary": 35200,
"birthDate": "2022-01-03T00:00:00.000Z",
"woman": true,
"managerID": 424,
"employerID": 21,
"photo": "[object Picture]",
"extra": null,
"employer": {
"__KEY": 21
},
"manager": {
"__KEY": 424
}
}
]
},
{
"firstName": "Gary",
"lastName": "Reichert",
"directReports": [
{
"ID": 428,
"firstName": "Rex",
"lastName": "Chance",
"salary": 71600,
"birthDate": "1968-08-09T00:00:00.000Z",
"woman": false,

"managerID": 425,
"employerID": 21,
"photo": "[object Picture]",
"extra": null,
"employer": {
"__KEY": 21
},
"manager": {
"__KEY": 425
}
},
{
"ID": 429,
"firstName": "Jenny",
"lastName": "Parks",
"salary": 51300,
"birthDate": "1984-05-25T00:00:00.000Z",
"woman": true,
"managerID": 425,
"employerID": 21,
"photo": "[object Picture]",
"extra": null,
"employer": {
"__KEY": 21
},
"manager": {
"__KEY": 425
}
}
]
}
]