CenterForDigitalHumanities / CenterForDigitalHumanities/deer

New Pattern for tracking Entities

Open
#96 0 comments 0 reactions 0 assignees View on GitHub
enhancement OSS
Dominant language
JavaScript
Stars
3
Forks
1
PR merge metrics
No merged PRs in 30d

Description

Alpha is a bit messy. The Elements always call for the fetch on a deer-id which leads to duplicates. There are lots of ways the object may be asked for:

* `fetch()` response: is a smaller object and represents more certainly how the object is stored in RERUM (or elsewhere, I suppose)
* `expand(obj)` returns a result that looks up all the annotations and attaches them to the fetch response creating a more complete object for rendering. This often uses filters that may change based on if the `matchOn` parameter requires the `creator`, the `generator`, or other property to match
* A use is sometimes created for a normalized version of the object that makes it simpler for Elements to render without having to do complicated types of lookups like `prop.key?.val ?? prop.key?.value ?? prop.key?.['@value'] ?? prop.key ?? prop`.
* Specific uses are also called for on certain properties and UTILS has helpers like `getValue()` or `getLabel()` that expose these.

In all these cases, if two Elements have matching `deer-id` attributes, the call for the object is made twice. Storing the object in the IDBStore helps, and the ServiceWorker prevents calling to the Internet twice, but the Element contains no reference to the Object in any form or vice versa.

Either the Elements or the Entities should probably be in control of the Truth and data managed through registration with this.

### The Plan

As the Elements can only outnumber the Entities, the Entities are probably a better place to centralize data control. Data flow then would look like this:

1. DOM Element with `deer-view` customization tries to register itself with the Entity map, announces its attachment with a `"view"` event posted to the web worker if one it not found.
2. The Web Worker first creates the Entity in the map.
1. Registers the Element with the Entity
2. Check IDBStore and enter result in map
3. Fires an `expand()` to fill out the Entity
1. The result from the `expand()` may fire a redraw on the Element

### Changes to alpha model

There are some pieces of the current setup that we need to break to enable this change:

#### `expand()` unparameterization

The current expansion looks for Annotations that are filtered through a `matchOn` Array and have some fragile use of the `historyWildcard` we use in so many places. Instead of doing all this work up front, we should create Annotations for all targeting Annotations and let the templates apply more filtering when rendering. Every result from the query should have an Annotation that also registers itself with the Entity it targets. Another issue may get into the weeds on this, as there is some combination of existence that should probably be honored... somewhere along this line, where newest wins:

* Match on `creator`, `__rerum.generatedBy`, and `body`
* Match on `__rerum.generatedBy` and `body`
* Match on `__rerum.generatedBy` and `getValue(body)`
* Match on `__rerum.generatedBy` or `getValue(body)`
* leaves (i.e., `__rerum.history.next === []`)
* leaves matching a `creator` or `__rerum.generatedBy`

#### Normalization

To make it easier for template creators to trust the shape of the data, one of the getters for the data in an Entity will normalize it, combined with all the Annotations. This will follow the patterns that are already set in `buildValueObject()` and not only put the value into a property, but also add metadata. There may be a need for some special handling tracked in other issues like language maps in `label`s etc. This is not something on each Annotation, but on the data object built from the Entity with each unique Annotation.

```js
{
@context: "default or from Entity record",
id: "URI which may be mapped from @id",
property: {
@context: "optional scoped context for this Annotation",
value: "value from Annotation which may be lots of forms, but will always be moved into this property",
source: {
citationSource: "Annotation id or whatever is defined on the Annotation already",
citationNote: "Annotation label or default assertion",
comment: "imported comment or link to DEER"
},
evidence: "URI to evidence, biblio entry or something else from Annotation"
}
}
```

This would be on the Entity class that is indexed in the Entity map as a getter, so `Entity.normalized` would process the `Entity.data` and expand it with the registered Annotations (and any constraints added by the Element template) to return something "normal" looking. This can set expectations for template authors to always find something meaningful at `property.value` for example.

#### Accessors and Methods

As described in [MSDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get), the Entity class will benefit from several accessor properties that will be enumerated elsewhere. The first is `normalized` which provides a version of the Entity that is suitable for templates. Others may be imagined.

The uniform methods around the Entities and Elements will help with moving around messages and Events:

* An Element registers itself with the Entity and is then subscribed to for any new Annotations or updates. This is mechanically done with Events which are probably best typed simply at the `id` of the Entity.
* An Entity broadcasts an update to itself, triggering redraws for the dependent Elements. This might be helped is part of the announcement was a diff from the last update or if that diff was available as a getter like `Entity.changes` or something, allowing a template to update piecemeal or not at all if changes do not apply.
* Finalizing changes so loaders can be removed or some level of certainty can be added to a rendering, which may have up to 3 redraws: first load from Map, new object from Internet, expanded Entity after Annotation query.

#### Storage

The elements needed for interaction will be loaded in several layers to provide quick access and ultimately even allow for offline use.

* *Custom Elements and Entity map* Every component that is a custom Element allows us to store some things along with the Elements in the DOM. As long as we remember to destroy these when they are removed, the memory used is minimal. This data is obliterated with each page load, so do not count on it. The Entity map is an `id` index of loaded objects of the type that would be the predicate of some `deer-id` attribute. Annotations are usually added to existing ones, but if there is a direct template of an Annotation then it may be entered in here.
* *IDBStore* is the persistent storage connected to a machine and can be used to reduce wait time for good draws. This storage will be used for objects like entity data responses from the Internet. I am not certain how Object references work, so I do not think it will be helpful to store the Entity map in there as a permanent thing, though it would save processing. Put a pin here.
* *RERUM* is the external place we keep shoving things. The IDBStore may be a way to make this something we sync with instead, so that offline use is more possible.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.