1. Datamodel
The objects in the easydb are based on a freely configurable schema. The schema defines different objecttypes, which are represented as database tables. Each objecttype consists of a number of fields (columns) of different types.
The objects are represented by one or more masks which can be configured to control which data is writable, readable and searchable.
Objecttypes and fields in the schema
Objecttypes are defined as tables in the schema. Each field in an objecttype is defined as a column with a specific name and type in the table.
The schema can be read and written using the API api/v1/schema/user
.
For this tutorial, we assume a simple datamodel with a main objecttype including text fields, file (asset), list of links and a link to a hierarchical object:
main
(main object)title
(text field: typetext_oneline
)description
(text field: typetext_oneline
)picture
(asset field: typeeas
)place
(field with a link to another objecttype:place
)keywords
(nested table: list of rows of fields)keyword
(link to another objecttype:keyword
)comment
(text field: typetext_oneline
)
keyword
(simple list object)name
(text field: typetext_oneline
)
place
(hierarchical list object)name
(text field: typetext_oneline
)
This datamodel is defined in this (simplified) JSON object. For the detailled overview of the schema structure, see Schema Attributes. The complete schema, maskset and localization keys can be found here.
{
"type": "user",
"tables": [
{
// main objecttype
"name": "main", // internal unique name of the table
"table_id": 1, // internal unique id of the table
"in_main_search": true, // this objecttype is included in the main fulltext search
"columns": [
{
"column_id": 1, // internal unique id of the column
"kind": "column", // kind "column" defines this as a simple field
"name": "title", // internal unique name of the column
"type": "text_oneline" // type of this field is a simple one line text
},
{
"column_id": 2,
"kind": "column",
"name": "description",
"type": "text_oneline"
},
{
"column_id": 3,
"kind": "column",
"name": "picture",
"type": "eas" // type of this field is a link to a file
},
{
"column_id": 4,
"kind": "column",
"name": "place",
"type": "link" // type of this field is a link to another objecttype "place"
},
{
"other_table_id": 2, // reference to the other nested table
"other_table_name_hint": "main__keywords",
"kind": "link" // kind "column" defines this as a link to another (nested) table
}
]
},
{
// nested table inside the main table
"name": "main__keywords",
"table_id": 2,
"owned_by": {
"other_table_id": 1, // defines this table as a sub (nested) table in the table "main"
"other_table_name_hint": "main"
},
"columns": [
{
"column_id": 5,
"kind": "column",
"name": "keyword",
"type": "link" // type of this field is a link to another objecttype "keyword"
},
{
"column_id": 6,
"kind": "column",
"name": "comment",
"type": "text_oneline"
}
],
"foreign_keys": [
{
// defines the link in column "keyword" as a foreign key
// column "keyword" links to the table "keyword"
"name": "main__keywords__keyword__keyword_fkey",
"columns": [
{
"column_id": 5,
"column_name_hint": "keyword"
}
],
"referenced_table": {
"table_id": 3,
"name_hint": "keyword",
"columns": [
{
"auto_column_primary_key": true
}
]
}
}
]
},
{
// simple objecttype
"name": "keyword",
"table_id": 3,
"columns": [
{
"column_id": 7,
"kind": "column",
"name": "name",
"type": "text_oneline",
"not_null": true // this field can not be empty
}
],
"unique_keys": [
{
"columns": [
{
"column_id": 7 // column 7 ("name") has a unique constraint
}
]
}
]
},
{
// simple hierarchical objecttype
"name": "place",
"table_id": 4,
"is_hierarchical": true, // this is a hierarchical objecttype, objects can be linked to other objects as their parent
"columns": [
{
"column_id": 8,
"kind": "column",
"name": "name",
"type": "text_oneline",
"not_null": true // this field can not be empty
}
]
}
]
}
⇑ go to beginning of this snippet
Fields in nested tables
To insert repeated rows of the same structur into a field, the field is linked to another (nested) table. The field is defined as "kind": "link"
and includes the internal id of the nested table it links to ("other_table_id"
).
In the example, the column "keywords"
in the objecttype "main"
contains another table which itself contains a link to the objecttype "keyword"
as well as a simple textfield. The object can contain an arbitrary number of rows of this structure.
The nested table itself ("main__keywords"
) is defined like any other schema table, but the key "owned_by"
which contains information about the table which links to this nested table, defines it as not an actual objecttype. The fields in this nested table are defined as columns like in any other table.
Masks for objecttypes
Masks are used to control fields of an objecttype. For each field, a mask contains information, wether the field is writable, readable or searchable, as well as other information used to display fields. All objects have at least one mask, but multiple masks can be defined to control access to objects using the rights management. One mask is always defined as the standard mask for this objecttype.
The server also always provides the out-of-the-box mask _all_fields
, which is no real mask, but used as an information for the server to include all fields without any limitations.
The maskset can be read and written using the API api/v1/mask
.
For the detailled overview of the maskset and mask structure, see Mask Definition.
{
"type": "user",
"masks": [
{
// first mask for objecttype "main"
"name": "main__standard", // internal unique name of the mask
"mask_id": 2, // internal unique id of the mask
"table_id": 1, // id of the referenced table "main"
"is_preferred": true, // this is the standard mask for this objecttype
"fields": [
{
"kind": "field",
"column_id": 1,
"edit": {
"mode": "edit", // this field is writable (and so automatically readable)
"group_edit": false
},
"output": { // information on how to display and render the field
"detail": true, // show the field in the detail view in the frontend
"text": true, // show the field in the text list view in the frontend
"table": true, // show the field in the table view in the frontend
"standard": {
"format": "comma", // this value is added to the standard of the object
"order": 1
}
},
"search": {
"expert": true, // this field is included in the expert search
"fulltext": true, // this field is included in the fulltext search
"facet": true // this field is included in the facet view (filter tree)
}
},
{
"kind": "field",
"column_id": 2,
"edit": {
"mode": "edit",
"group_edit": false
},
"output": {
"detail": true,
"text": true,
"table": true,
"standard": {
"format": "comma",
"order": 2
}
},
"search": {
"expert": true,
"fulltext": true,
"facet": true
}
},
{
"kind": "field",
"column_id": 3,
"edit": {
"mode": "edit",
"group_edit": false
},
"output": {
"detail": true,
"text": true,
"table": true,
"standard": {
"format": "comma"
},
"standard_eas": {
"order": 1 // this asset (small version) is shown in the standard of the object
}
},
"search": {
"expert": true,
"fulltext": true,
"facet": true
}
},
{
"kind": "link",
"column_id": 4,
"other_table_id": 4, // this field is a link to another table (id 4, "place")
"mask_id": "PREFERRED", // id of the mask of the linked objecttype which is used to render the link, use standard mask
"edit": {
"mode": "edit",
"group_edit": false
},
"output": {
"detail": true,
"text": true,
"table": true,
"standard": {
"format": "comma"
}
},
"search": {
"expert": true,
"fulltext": true,
"facet": true,
"nested": false
}
},
{
"kind": "linked-table", // this field is a nested table
"other_table_id": 2, //
"mask": {
// inline mask definition for the fields in the nested table
},
"edit": {
"mode": "edit",
"append_only": false,
"as_table": false,
"show_labels": false
},
"output": {
"detail": true,
"text": true,
"table": true,
"standard": {
"format": "comma"
}
},
"search": {
"expert": true,
"fulltext": false,
"facet": false
}
}
]
},
{
"name": "main__simple", // internal unique name of the mask
"mask_id": 4, // internal unique id of the mask
"table_id": 1, // id of the referenced table "main"
"fields": [
// all fields that are neither writable nor readable are skipped here,
// in the actual maskset these fields have the attribute "edit.mode": "off"
{
"kind": "field",
"column_id": 1,
"edit": {
"mode": "show", // this field can only be seen but not edited, if the object is rendered in this mask
"group_edit": false
},
"output": {
"detail": true,
"text": true,
"table": true,
"standard": {
"format": "comma",
"order": 1
}
},
"search": {
"expert": true,
"fulltext": true,
"facet": false
}
},
{
"kind": "field",
"column_id": 3,
"edit": {
"mode": "show", // this field can only be seen but not edited, if the object is rendered in this mask
"group_edit": false
},
"output": {
"detail": true,
"text": true,
"table": true,
"standard": {
"format": "comma"
},
"standard_eas": {
"order": 1
}
},
"search": {
"expert": true,
"fulltext": true,
"facet": true
}
}
]
}
]
}
⇑ go to beginning of this snippet
The objecttype "main"
in the example has two masks.
Mask "main__standard"
contains all fields, the asset, links and nested tables. These fields are writable ("edit": { "mode": "edit" }
), which automatically means they are also readable. All fields are also included in the expert, fulltext and facet search. This mask is also defined as the standard (preferred) mask for this objecttype.
The other mask "main__simple"
excludes most fields, and only the main title field "title"
and the image "picture"
is shown. These fields are not writable but only readable ("edit": { "mode": "show" }
). All other fields are not included in the object at all ("edit": { "mode": "off" }
), so they can not be read or written.
Using the rights management, the following scenario can be set up:
Allow certain users or groups to see the "main__standard"
mask, so they can see and edit all fields. Other users or groups can be restricted, so they can only see objects in the "main__simple"
mask, so these users can only see some fields of the object and can not edit any data in the object.
Localization
The names of objecttypes, fields and masks we used so far are technical names which are used internally in the database and the JSON objects. To render human readable localizations in different languages, the translations can assigned to the fields. JSON keys are formatted in a specific way (see below), and contain a simple object where each key is a specific l10n key which then contains the translation.
The localizations can be read and written using the API api/v1/l10n/user
.
{
"schema.main.name": { // names for the "main" objecttype
"de-DE": "Hauptobjekt",
"en-US": "Main"
},
"schema.main.column.title": { // names for the column "title" in the "main" objecttype
"de-DE": "Titel",
"en-US": "Title"
},
"schema.main.column.picture": { // names for the asset column "picture" in the "main" objecttype
"de-DE": "Bild",
"en-US": "Picture"
},
"schema.main.column.main__keywords": { // names for the nested table "main__keywords" in the "main" objecttype
"de-DE": "Schlagwörter",
"en-US": "Keywords"
},
"schema.main__keywords.column.comment": { // names for the "comment" column in the nested table "main__keywords" in the "main" objecttype
"de-DE": "Kommentar zum Schlagwort",
"en-US": "Comment for keyword"
},
"mask.1.main__standard.name": { // names of the "main_standard" mask for the "main" objecttype
"de-DE": "Standard",
"en-US": "Standard"
},
"mask.1.main__simple.name": {
"de-DE": "Vereinfacht",
"en-US": "Simple"
}
}
⇑ go to beginning of this snippet
Objecttype Names
Objecttype names are stored at keys of the format "schema.<table name>.name"
.
In this example, the translation for the "main"
objecttype is found at the key "schema.main.name"
and contains the german translation “Hauptobjekt” ("de-DE"
), and the english translation “Main” ("en-US"
).
Column Names
Column names are stored at keys of the format "schema.<table name>.column.<column name>"
. This applies to actual objecttypes as well as for nested tables.
Mask Names
Mask names are stored at keys of the format "mask.<mask id>.<mask name>.name"
.