Nested Fields

Nested fields type documentation.

Overview

This field type allows for the inclusion of other fields within it, facilitating nested structures. This capability permits the creation of multiple levels of nesting, thereby supporting complex data structures within entities.

For instance, you can create a structure like the following:

  • name
  • email
  • address
    • addressLine
    • zipCode
    • state

In this scenario, “address” is a field of type nested fields and contains the fields “addressLine,” “zipCode,” and “state.”

Furthermore, these nested structures can also be multi-valued, as demonstrated in the following example:

  • name
  • email
  • addresses
    • addressLine
    • zipCode
    • state
    • primary

In this instance, you can store multiple addresses within the “addresses” field. Feel free to use the corrected version above to update your documentation.

Available features

NameSupported
Many multiplicityyes
Default valuesno
Unique flagno
Required flagyes
Indexable flagno
Sensitive flagno
Calculated valueyes
Automatic initializationno
Calculated initial valueno
Aggregationno
Default type rulesno
Default display optionsno

Type rules

Label

This option allows you to assign a label to the group of fields. This label can be employed throughout the application to display a preview of the nested fields or to facilitate searches across records.

There are three available options:

  • None: No label will be generated. In the user interface, an auto-generated label might appear based on the field’s name and the index if it’s a multi-valued field.

  • Field: A nested field can be chosen, and its value will be used as the label.

  • Script: A script can be utilized to generate the label:


    Parameters
    NameTypeDescription
    recordsys.data.RecordThe record the field belongs to.
    nestedsys.data.RecordThis is a sys.data.Record object, with the distinction that the root of the record is designated to the nested fields. To illustrate, consider the subsequent structure:
    - name
    - email
    - addresses
    - addressLine
    - zipCode
    - state
    Suppose the field “addresses” is multi-valued, and you desire the label to be the “state.” In this situation, you cannot utilize the record variable since you aren’t aware of which address you are generating the label for. The solution entails employing the nested variable, which inherently points to the address you are calculating the label for. This could be achieved as follows:
     return nested.field(‘state’).name(); 
    Returns

    string: You should return the label

    Samples
    // uses the state and zip code as the label
    return nested.field('state').val()+'-'+nested.field('zipCode').val();
    


Display options

Show as accordion

Enabling this option allows the group of fields to be collapsible and expandable, with the label serving as the title of the accordion.

Indent fields

Enabling this option causes the inner fields to be indented in the UI, aligning them to the right of the parent field’s label.

If this option isn’t enabled, the label of the parent field will be displayed at the top, and the inner fields will be rendered below without indentation.

Inner fields layout

This setting controls the layout of inner fields, which proves especially useful when space is limited. The available options are:

  • Automatic: Based on the view’s context, either a fixed or dynamic layout will be selected. For example, the dynamic layout will be used in the grid view, while the fixed layout will be chosen in read-only views.
  • Fixed: Labels are allocated a predetermined amount of space, with the remaining space reserved for the values of the inner fields.
  • Dynamic: The space allotted for inner field labels and values adjusts based on the content in each column.
  • Custom: In this scenario, you can specify the width assigned to inner field labels, while the remaining space is designated for the values.

REST API

Read format

The data is represented in JSON format and comprises the following fields:

  • id: This serves as the identifier for the nested group, primarily intended for internal use.
  • label: The label associated with the nested group.
  • Inner fields: Each inner field is contained within the map structure. In the example provided below, the fields addressLine, zipCode, city, and state are considered inner fields.

Here is a sample representation of a nested field in JSON format:

"address": {
  "id": "5877e2bd41058800079d11cc",
  "label": "WV - 25335",
  "addressLine": "3351 Harper Center",
  "zipCode": "25335",
  "city": "Charleston",
  "state": "WV"
}

Write format

You can set a nested field by providing it’s inner fields:

"address": {
  "addressLine": "3351 Harper Center",
  "zipCode": "25335",
  "city": "Charleston",
  "state": "WV"
}

When updating a multi-valued nested field, it is crucial to include the corresponding id:

"addresses": [
  {
    "id": "57fd45aee4b0ce322b0c86ac",
    "addressLine": "4 Crownhardt Plaza",
    "zipCode": "73157",
    "city": "Oklahoma City",
    "state": "OK"
  },
  {
    "id": "57fd45aee4b0ce322b0c86aa",
    "addressLine": "4 Magdeline Place",
    "zipCode": "89145",
    "city": "Las Vegas",
    "state": "NV"
  }
]

If the id is not provided, the system will interpret the absence of the id as a request to delete the existing entry and add a new one, which will result in assigning a new ID to the entry.

Please note that you should refrain from sending the label, as it is automatically calculated and managed.

JavaScript API

Read format

The val() method within the wrapper will return an object:

{
  "addressLine": "803 Anthes Pass",
  "zipCode": "08608",
  "city": "Trenton",
  "state": "NJ"
}

In essence, this represents an object with inner fields contained within.

However, rather than directly retrieving the raw value of the relationship in most instances, you will typically utilize the methods provided within the wrapper to directly access the ID or label.

Write format

When configuring nested fields, you should adhere to the same format as used for reading:

{
  "addressLine": "803 Anthes Pass",
  "zipCode": "08608",
  "city": "Trenton",
  "state": "NJ"
}

For example:

contact.field('address').val({
  "addressLine": "803 Anthes Pass",
  "zipCode": "08608",
  "city": "Trenton",
  "state": "NJ"
});

If the field is multi-valued, you have the option to specify the index:

company.field('addresses[1]').val({
  "addressLine": "803 Anthes Pass",
  "zipCode": "08608",
  "city": "Trenton",
  "state": "NJ"
});

For multi-valued fields, you can also leverage the utilities provided by sys.data.ManyWrapper.

Wrapper method:
field(path)

Returns the wrapper of an inner field.

Parameters
NameTypeRequiredDescription
pathstringyesThe path of the inner field to get the wrapper.
Returns

sys.data.Wrapper or sys.data.ManyWrapper - The wrapper of the inner field.

Exceptions

badRequest

If path is invalid

Samples
// gets the wrapper of the inner field and prints its value
var contact = sys.data.findOne('contacts', {email: 'djohnstonb0@behance.net'});
var stateWrapper = contact.field('address').field('state');
log('state: '+stateWrapper.val());


Wrapper method:
id()

Returns a unique ID of the nested field instance.

Returns

string - The ID of the nested field instance.

Samples
// prints the ID of the nested field
var record = sys.data.findOne('contacts', {email: 'djohnstonb0@behance.net'});
log('address id: '+record.field('address').id());


Wrapper method:
label()

Returns the label of the nested field or null if the field is empty.

Returns

string - The label of the nested field as configured in type rules.

Samples
// gets the wrapper of a nested field and prints its label
var contact = sys.data.findOne('contacts', {email: 'djohnstonb0@behance.net'});
var addressWrapper = contact.field('address');
log('state: '+addressWrapper.label());


Export/Import

Export format

Nested fields themselves cannot be directly exported. What is exported are the inner fields:

"nested.innerField1","nested.innerField2"
"value1","value2"

Import format

Nested fields themselves cannot be directly imported. What is imported are the inner fields:

"nested.innerField1","nested.innerField2"
"value1","value2"

If there is ambiguity, the name takes precedence over the label.

Queries

For more information, please refer to the Query Language Documentation.

Available operators

OperatorSupported
equalsyes
notEqualsno
emptyyes
notEmptyyes
likeno
greaterno
greaterOrEqualsno
lessno
lessOrEqualsno
betweenno
currentUserFieldno

Query formats

Queries on parent nested fields are not supported. However, you can perform queries using inner fields:

// filters by state 
var records_sample = sys.data.find('contacts', {'address.state': 'NY'});
log('total: '+records_sample.count());
while (records_sample.hasNext()) {
    log(records_sample.next().label());
}
// filters by state 
var query_sample = sys.data.createQuery('contacts')
    .field('address.state').equals('NY')
var records_sample = sys.data.find(query_sample);
log('total: '+records_sample.count());
while (records_sample.hasNext()) {
    log(records_sample.next().label());
}
// filters by state 
GET /data/contacts?address.state=NY

In these cases, you should employ the format and operators corresponding to the type of inner fields.

Furthermore, querying is feasible even when dealing with multi-valued nested fields:

// filters by state 
var records_sample2 = sys.data.find('companies', {'addresses.state': 'NY'});
log('total: '+records_sample2.count());
while (records_sample2.hasNext()) {
    log(records_sample2.next().label());
}
// filters by state 
var query_sample2 = sys.data.createQuery('companies')
    .field('addresses.state').equals('NY')
var records_sample2 = sys.data.find(query_sample2);
log('total: '+records_sample2.count());
while (records_sample2.hasNext()) {
    log(records_sample2.next().label());
}
// filters by state 
GET /data/contacts?addresses.state=NY

In this scenario, since “addresses” is multi-valued, any company that possesses at least one address with “NY” in the “state” field will be encompassed in the results.

Finally, for querying nested record fields simultaneously:

// filters by street and zip code 
var records_sample3 = sys.data.find('companies', {'addresses': {'street': 'Street 123', 'zipCode': '1234'}});
log('total: '+records_sample3.count());
while (records_sample3.hasNext()) {
    log(records_sample3.next().label());
}
// filters by street and zip code 
var query_sample3 = sys.data.createQuery('companies')
    .field('addresses').equals({'street': 'Street 123', 'zipCode': '1234'})
var records_sample3 = sys.data.find(query_sample3);
log('total: '+records_sample3.count());
while (records_sample3.hasNext()) {
    log(records_sample3.next().label());
}
// filters by street and zip code 
GET /data/contacts?addresses={same-as-query-builder}

Aggregate queries

Please refer to the Aggregate Queries Documentation for more detailed information.

Available operators

OperatorSupported
sumno
avgno
firstyes
lastyes
minno
maxno

UI queries

Please refer to the UI Queries Documentation for more detailed information.

Matching of values

PropertyDescription
Matching operatornone
Nested fields do not support UI queries.

Available operators

OperatorSupported
Many valuesno
Greaterno
Greater or equalsno
Lessno
Less or equalsno
Betweenno