Skip to content

Field pages

joschne edited this page Jul 10, 2024 · 7 revisions

Field pages are a core element of the Geovistory toolbox. They are used to load and display graph data as (nested) lists, and the are used to synchronize data from server to client for collaborative data editing.

Terminology

Field In Geovistory a field is the set of statements of the same source entity, property, direction and scope (~ the project).

Field page is a field plus limit and offset, defining a subset of statements of the field.

Field pages in UI

The following image depicts a entity card in the Geovistory UI. The card is composed by a header with the entity label and edit buttons. What you see below, are fields many field pages, put together.

jakarta

Let's focus on the "is departure place of voyage". This is a field page, defined as:

  • source entity: 207386 (Jakarta ID)
  • property: 1335 ("is departure place of voyage")
  • direction: incoming (because the source entity is the range / object)
  • limit: 5 (Items per page = 5)
  • offset: 0 (we see the first page)

In this field page, we see five rows, each of them representing a statement with a ship voyage as its target entity. You can also see a paginator: The UI knows the total number of statement in the field: 2295, of which the first 5 are visible.

There are nested fields. Let's focus at the first row. To the left, there is a star-symbol. This symbol represents the ship voayage. We do not see the id number of this entity, but you could click and open it in a separate tab and see its id: 158062. This is the source entity for the fields within that row.

The first field, "has arrival place", of the first row is another field page, defined as:

  • source entity: 158062 (a Ship Voyage)
  • property: 1336 ("has arrival place)
  • direction: outgoing (because the source entity is the domain / subject)
  • limit: 1 (implicit)
  • offset: 0 (implicit)

Field pages are not always nested:

shipvoyages

On entity card of a ship voyage, the "has arrival place" field page just displays the entity preview of the target entity "Wielingen BE". No nested fields for "Wielingen BE" are loaded.

To sum up: An entire entiy card is composed by many field pages that are potentially nested.

UI Components

The field pages are displayed by a number of components starting with view-field-*. The component that requests the loading of the field page is view-field-body.component. Each field you see in the screenshot above, contains such a component.

UI Services

Since we can access the same field in multiple places, there is a shared service PaginationService that deals with field load requests. The service does three things:

  • It maintains a cache of field load requests and blocks duplicated request from propagating to the Rest API
  • It talks to the state facade to make the API calls and store the result in the redux store
  • It listens to field change events and compares the change timestamps to the cached validFor of the loaded field pages, in order to determine field page reloads.

Rest API

The API to load field pages is accessible at /subfield-page/load-subfield-page.

You find the technical documentation here:

[LoopBack API Explorer](https://toolbox.geovistory.org/api/explorer/#/SubfieldPageController/SubfieldPageController.loadSubfieldPages)

Request

The request body is of type GvFieldPageReq: Bildschirm­foto 2024-07-09 um 15 10 29

The request object has three main members.

req.pkProject is used to check permissions of the requester to access the requested field pages

req.page identifies the page

  • req.page.source The source entity is identified by the id given by Geovistory Toolbox. An information.statement can have subjects/objects in different tables. Therefore we use an object to identify the source:

    • fkInfo references an entity in schema information (actually exclusively in table information.resource)

    • fkData references an entity in schema data

    • fkTablesRow references a record in tables.row

    • fkTablesCell references a record in tables.cell

  • req.page.property The property id given by OntoME. (fkPropertyOfProperty is never used)

  • req.page.isOutgoing defines the direction of the statements in the field. If true, the source entity is the subject of the statements, else the object.

  • req.page.scope is an additional filter on the statements. The object may only have one of the following keys filled:

    • inProject only statements of this project id are included from the field page

    • notInProject statements of this project id are excluded from the field page

    • inRepo statements present in the community graph are taken

    • noConstraint take all statements

req.targets is an object where the key references the class id of the target entity and the value is of type GvFieldTargetViewType. Since the ontology allows one property to have different range/domain classes, using sub-classes, the classes of the target entities of the different statements may differ, even within one field. For each target class, you can define the target.

  • req.targets[class_id]
    • {"appellation": "true"} the target view type is information.appellation
    • {"language": "true"} the target view type is information.language
    • {"place": "true"} the target view type is information.place
    • {"timePrimitive": "true"} the target view type is information.timePrimitive
    • {"langString": "true"} the target view type is information.langString
    • {"dimension": "true"} the target view type is information.dimension
    • {"cell": "true"} the target view type is tables.cell
    • {"entityPreview": "true"} the target view type is pgwar.entityPreview
    • {"nestedResource": [...]} the target view type is an entity, for which individual pages are requested, see below
    • {"subReqsRecursiveTargets": "true"} this target view type is never used!

[JS 24/07/10] Currently, the server joins the correct target data independent from the request. Only if "nestedResource" is set, the serve queries the nested fields of that resource. All other GvFieldTargetViewTypes are ignored on the server. But the server returns the request in its response. -> Why are these GvFieldTargetViewTypes passed from client to server and back? -> here we may generate a lot of overhead ?

Response

Bildschirm­foto 2024-07-10 um 10 08 30

The response contains one member subfieldPages containing an array of GvSubfieldPageInfo.

Due to a limitation in Loopback, it was not possible to return an array. So this wrapper {subfieldPages: [] } is a workaround to ship the array of field pages to the client.

Each GvSubfieldPageInfo contains the following properties:

  • req: the original request that caused this response [JS: we have to analyse client first to understand why needed. Maybe we can optimize this! and not return the request back to client.]

  • count: the total number of statements the field without the limit/offset. This is useful to make a paginator.

  • paginatedStatements: array of StatementWithTarget containing the data to display one statement and its target. It is an object containing elements in the form of the database schema: InfStatement, ProInfoProjRel and the target model, so that these elements can be put in to the redux store.
    [JS: this may be considered an overhead: we join the data in postgres, then decompose it in postgres into the individual pieces (records of tables) and send it to the client. The client then re-joins them using RxJS queries. This is additional logic, mainly on the client. With the new pgwar we could simplify a lot: We could replace StatementWithTarget with a ORM-model for pgwar.v_statements_combined, WarStatement, and use this model even in the redux store in the client. ]

    • statement: the InfStatement with subject, property, object

    • projRel: the relationship between the statement and the project.

    • isOutgoing: the direction of the StatementWithTarget. If true, the target is the object, else the subject.

    • ordNum: the position of the statement within the page, when ordered according to the users preference of ordering. (Users can drag and drop statements in the field in UI to change the ordNum )

    • targetLabel: The label of the target. In case of a literal target (appellation, place, dimension, time primitve, language, langugage string or cell) the label is deduced from the literal, in case of an entity, it is the entity label of the entity preview, as it is created by the warehouse.

    • targetClass: the id of the target class

    • target: the target of the statement (a StatementTarget) can be...

      • ... a literal {"appellation": InfAppellation};

        {"dimension": StatementTargetDimension};

        {"langString": StatementTargetLangString};

        {"language": InfLanguage};

        {"timePrimitive": StatementTargeTimePrimitive};

        {"place": InfPlace};

        {"cell": TabCell};

      • ... an entity {"entity": StatementTargetEntity};

  • validFor: Timestamp of the start of the query. This is relevant for the realtime update mechanism. The client, receiving this response, can assume, that the response is valid until this timestamp. In case there is a field change with a newer timestamp, the field page needs a reload.

Example (simple)

Request one field page without specified targets:

[
  {
    "pkProject": 924033,
    "page": {
      "source": { "fkInfo": 869583 },
      "property": {
        "fkProperty": 1436
      },
      "isOutgoing": false,
      "limit": 1,
      "offset": 0,
      "scope": { "inProject": 924033 }
    },
    "targets": {}
  }
]

Response

{
  "subfieldPages": [
    {
      "validFor": "2024-07-10T08:06:35.519Z",
      "paginatedStatements": [
        {
          "projRel": {
            "fk_entity": 869596,
            "pk_entity": 942247,
            "fk_creator": 235,
            "fk_project": 924033,
            "is_in_project": true,
            "fk_last_modifier": 235,
            "is_standard_in_project": false
          },
          "isOutgoing": false,
          "target": {
            "entity": {
              "resource": {
                "fk_class": 633,
                "pk_entity": 869595,
                "community_visibility": {
                  "dataApi": false,
                  "toolbox": true,
                  "website": false
                }
              },
              "entityPreview": {
                "fk_class": 633,
                "pk_entity": 869595,
                "fk_project": 0,
                "class_label": "Relationship",
                "entity_type": "teEn",
                "entity_label": "Abel Heusler-Geigy, Anna Elisabeth Heusler-Geigy",
                "tmsp_last_modification": "2024-07-09T09:10:17.308607+00:00"
              }
            }
          },
          "targetClass": 633,
          "targetLabel": "Abel Heusler-Geigy, Anna Elisabeth Heusler-Geigy",
          "statement": {
            "pk_entity": 869596,
            "fk_property": 1436,
            "fk_object_data": 0,
            "fk_object_info": 869583,
            "fk_subject_data": 0,
            "fk_subject_info": 869595,
            "is_in_project_count": 1,
            "fk_object_tables_row": 0,
            "fk_object_tables_cell": 0,
            "fk_subject_tables_row": 0,
            "fk_subject_tables_cell": 0,
            "fk_property_of_property": 0,
            "is_standard_in_project_count": 0
          }
        }
      ],
      "count": 1,
      "req": {
        "pkProject": 924033,
        "page": {
          "source": {
            "fkInfo": 869583
          },
          "property": {
            "fkProperty": 1436
          },
          "isOutgoing": false,
          "limit": 1,
          "offset": 0,
          "scope": {
            "inProject": 924033
          }
        },
        "targets": {}
      }
    }
  ]
}

Example (nested)

Request

[
  {
    "pkProject": 924033,
    "page": {
      "source": { "fkInfo": 869583 },
      "property": {
        "fkProperty": 1436
      },
      "isOutgoing": false,
      "limit": 1,
      "offset": 0,
      "scope": { "inProject": 924033 }
    },
    "targets": {
      "633": {
        "nestedResource": [
            {
                "targets": {},
                "page": {
                    "property": {
                        "fkProperty": 1436
                    },
                    "isOutgoing": true,
                    "limit": 1,
                    "offset": 0,
                    "isCircular": true
                }
            }
          ]
        }
    }
  }
]

Response

{
  "subfieldPages": [
    {
      "validFor": "2024-07-10T09:26:21.000Z",
      "paginatedStatements": [
        {
          "projRel": {
            "fk_entity": 869596,
            "pk_entity": 942247,
            "fk_creator": 235,
            "fk_project": 924033,
            "is_in_project": true,
            "fk_last_modifier": 235,
            "is_standard_in_project": false
          },
          "isOutgoing": false,
          "target": {
            "entity": {
              "resource": {
                "fk_class": 633,
                "pk_entity": 869595,
                "community_visibility": {
                  "dataApi": false,
                  "toolbox": true,
                  "website": false
                }
              },
              "entityPreview": {
                "fk_class": 633,
                "pk_entity": 869595,
                "fk_project": 0,
                "class_label": "Relationship",
                "entity_type": "teEn",
                "entity_label": "Abel Heusler-Geigy, Anna Elisabeth Heusler-Geigy",
                "tmsp_last_modification": "2024-07-09T09:10:17.308607+00:00"
              }
            }
          },
          "targetClass": 633,
          "targetLabel": "Abel Heusler-Geigy, Anna Elisabeth Heusler-Geigy",
          "statement": {
            "pk_entity": 869596,
            "fk_property": 1436,
            "fk_object_data": 0,
            "fk_object_info": 869583,
            "fk_subject_data": 0,
            "fk_subject_info": 869595,
            "is_in_project_count": 1,
            "fk_object_tables_row": 0,
            "fk_object_tables_cell": 0,
            "fk_subject_tables_row": 0,
            "fk_subject_tables_cell": 0,
            "fk_property_of_property": 0,
            "is_standard_in_project_count": 0
          }
        }
      ],
      "count": 1,
      "req": {
        "pkProject": 924033,
        "page": {
          "source": {
            "fkInfo": 869583
          },
          "property": {
            "fkProperty": 1436
          },
          "isOutgoing": false,
          "limit": 1,
          "offset": 0,
          "scope": {
            "inProject": 924033
          }
        },
        "targets": {
          "633": {
            "nestedResource": [
              {
                "targets": {},
                "page": {
                  "property": {
                    "fkProperty": 1436
                  },
                  "isOutgoing": true,
                  "limit": 1,
                  "offset": 0,
                  "isCircular": true
                }
              }
            ]
          }
        }
      }
    },
    {
      "validFor": "2024-07-10T09:26:21.084Z",
      "paginatedStatements": [
        {
          "projRel": {
            "fk_entity": 869604,
            "pk_entity": 942254,
            "fk_creator": 235,
            "fk_project": 924033,
            "is_in_project": true,
            "fk_last_modifier": 235,
            "is_standard_in_project": false
          },
          "isOutgoing": true,
          "target": {
            "entity": {
              "resource": {
                "fk_class": 21,
                "pk_entity": 869598,
                "community_visibility": {
                  "dataApi": true,
                  "toolbox": true,
                  "website": false
                }
              },
              "entityPreview": {
                "fk_class": 21,
                "pk_entity": 869598,
                "fk_project": 0,
                "class_label": "Person",
                "entity_type": "peIt",
                "entity_label": "Anna Elisabeth Heusler-Geigy",
                "tmsp_last_modification": "2024-07-09T09:10:17.308607+00:00"
              }
            }
          },
          "targetClass": 21,
          "targetLabel": "Anna Elisabeth Heusler-Geigy",
          "statement": {
            "pk_entity": 869604,
            "fk_property": 1436,
            "fk_object_data": 0,
            "fk_object_info": 869598,
            "fk_subject_data": 0,
            "fk_subject_info": 869595,
            "is_in_project_count": 1,
            "fk_object_tables_row": 0,
            "fk_object_tables_cell": 0,
            "fk_subject_tables_row": 0,
            "fk_subject_tables_cell": 0,
            "fk_property_of_property": 0,
            "is_standard_in_project_count": 0
          }
        }
      ],
      "count": 2,
      "req": {
        "page": {
          "property": {
            "fkProperty": 1436
          },
          "isOutgoing": true,
          "limit": 1,
          "offset": 0,
          "isCircular": true,
          "scope": {
            "inProject": 924033
          },
          "source": {
            "fkInfo": 869595
          }
        },
        "targets": {},
        "pkProject": 924033
      }
    }
  ]
}

The response contains two field pages: One for each requested field.

Clone this wiki locally