Skip to content

Community metadata extensions in B2INST#

Community metadata extensions add fields to an instrument’s standard PIDINST metadata. For example, Sensor.Community records a sensor’s location, station, environment, and mobility. Use the standard instrument fields for information already covered by PIDINST, such as the instrument name, manufacturer, model, and owner.

This guide is for community representatives preparing an extension with the B2INST service administrators. It describes the implementation in the B2INST v3 codebase. Acceptance of a schema definition, display in the deposit form, validation of submitted values, and search indexing are separate capabilities; the current limitations are explained below.

For day-to-day community management, see the community guide. For entering instrument metadata, see the user guide.

Preparing and installing a community metadata extension#

Agree on the fields and their intended values with the community, then send the JSON definition to the B2INST service administrators. Community ownership does not provide a schema editor in the community settings. The current schema upload API is incomplete, so it should not be used as a self-service installation workflow.

The administrator can install a definition from the configured deployment environment:

invenio community-extensions add '<community-id>' sensorcommunity.json

For the Sensor.Community example below, replace <community-id> with that community’s ID and save the definition as sensorcommunity.json. The command checks the definition against that community, stores a new schema version, and prints its UUID. Installing another version does not rewrite existing instrument records. Keep the UUID associated with each record when interpreting its extension values.

Test the form, draft save, publication, display, and search with representative values on the test service before adopting an extension for routine use.

Schema structure and field names#

The definition has three required top-level entries:

Entry Meaning
community_slug The community’s exact slug, as returned by GET /api/communities/<community-id-or-slug>.
section Heading for the extension section in the instrument form.
fields A list of field definitions.

Each field requires name, type, ui_widget, and ui_props. The ui_props object must contain a label; it can also contain a description, placeholder, and required flag. There is no automatic default widget selected by the schema validator. Use a widget available in the deployed form and test it with the field’s value shape.

Use the lowercase community slug followed by a colon for every top-level field, for example sensorcommunity:station. A nested field’s children use local names such as latitude without the prefix. Although the definition validator exempts top-level nested fields from its prefix check, give those fields the prefix too: record updates use the prefix to associate extension values with the selected community.

Supported field definitions#

type Declared value Options and limits
text A string field_cls may be stripped, url, email, or enum. Omission uses sanitised text. For enum, provide options.
date A date The backend field builder uses a date value; its date format is YYYY-MM-DD. The exported JSON Schema currently describes it differently; see validation below.
integer A whole number Use a JSON number, rather than a quoted numeric string, in API values.
boolean true or false Use JSON booleans in API values.
vocabulary A controlled value Supply either a non-empty options list or a vocabulary_id available in B2INST.
nested An object or repeated objects Define child fields in properties. Form support has additional limits described below.

number is not an accepted type in this implementation. There is no declared decimal number field or dedicated date-interval or coordinate-pair type. Agree an appropriate representation with the service administrators rather than copying unsupported field types into a definition.

For a small controlled list, use type: "vocabulary" with options and a Dropdown widget. Put options on the field definition, not inside ui_props. A vocabulary_id refers to a vocabulary already configured in the service; it is not a URL for an arbitrary external vocabulary. If both are supplied, the builder chooses vocabulary_id.

Required fields and multiple values#

Set ui_props.required to true to declare a required field. This also supplies the required indication to the form. It is not a guarantee that every API submission will be checked against the extension definition; see validation.

Set the field’s top-level multiple to true to declare an array. For widgets that use it, such as Dropdown, also set ui_props.multiple to true: the builder does not copy the top-level flag into the widget props.

The backend builder can represent repeated text, dates, integers, booleans, and vocabulary values. This does not automatically provide a suitable repeating form control for each type. MultiLineInput is the custom repeating text control; a multi-select Dropdown can be used for a list of explicit vocabulary options. Test other combinations before offering them to users.

A required array is not automatically required to contain an item. Likewise, marking a text field as required does not impose a non-empty-string or minimum-length rule.

Complete community metadata extension example#

This definition is for Sensor.Community, whose community slug is sensorcommunity. It is taken from migration/migration_files/schema_extensions/sensorcommunity.json in the B2INST deployment and defines the Sensor Location Metadata section shown in the screenshot below. It includes repeated locations, a station number, environment and mobility options, sensor height, and descriptions of traffic and location.

Save this JSON as sensorcommunity.json. To adapt it for another community, replace community_slug and every sensorcommunity: prefix with that community’s exact slug.

{
  "community_slug": "sensorcommunity",
  "section": "Sensor Location Metadata",
  "fields": [
    {
      "name": "sensorcommunity:location",
      "type": "nested",
      "multiple": true,
      "use_as_filter": false,
      "ui_widget": "MultiNestedInput",
      "ui_props": {
        "label": "Location",
        "icon": "map marker alternate",
        "description": "Geographic position of the sensor.",
        "required": false,
        "multiple": true,
        "properties": {
          "_object_name": "Location"
        }
      },
      "properties": [
        {
          "name": "latitude",
          "type": "text",
          "use_as_filter": false,
          "ui_widget": "Input",
          "ui_props": {
            "label": "Latitude",
            "description": "Latitude in decimal degrees (-90 to 90)."
          },
          "field_cls": "stripped"
        },
        {
          "name": "longitude",
          "type": "text",
          "use_as_filter": false,
          "ui_widget": "Input",
          "ui_props": {
            "label": "Longitude",
            "description": "Longitude in decimal degrees (-180 to 180)."
          },
          "field_cls": "stripped"
        }
      ]
    },
    {
      "name": "sensorcommunity:station",
      "type": "integer",
      "multiple": false,
      "use_as_filter": false,
      "ui_widget": "Input",
      "ui_props": {
        "label": "Station",
        "icon": "hashtag",
        "description": "Station number at which the sensor is installed (non-negative).",
        "required": false,
        "multiple": false
      }
    },
    {
      "name": "sensorcommunity:environment",
      "type": "vocabulary",
      "field_cls": "enum",
      "options": [
        "indoor",
        "outdoor"
      ],
      "multiple": false,
      "use_as_filter": false,
      "ui_widget": "Dropdown",
      "ui_props": {
        "label": "Environment",
        "icon": "tree",
        "description": "Whether the sensor is positioned indoors or outdoors.",
        "required": false,
        "multiple": false,
        "clearable": true
      }
    },
    {
      "name": "sensorcommunity:mobility",
      "type": "vocabulary",
      "field_cls": "enum",
      "options": [
        "stationary",
        "mobile"
      ],
      "multiple": false,
      "use_as_filter": false,
      "ui_widget": "Dropdown",
      "ui_props": {
        "label": "Mobility",
        "icon": "exchange",
        "description": "Whether the sensor is stationary or mobile (default is stationary).",
        "required": false,
        "multiple": false,
        "clearable": true
      }
    },
    {
      "name": "sensorcommunity:sensorLevelAboveGround",
      "type": "integer",
      "multiple": false,
      "use_as_filter": false,
      "ui_widget": "Input",
      "ui_props": {
        "label": "Sensor Level Above Ground (cm)",
        "icon": "arrows alternate vertical",
        "description": "Height of the sensor above ground in centimeters (non-negative).",
        "required": false,
        "multiple": false
      }
    },
    {
      "name": "sensorcommunity:sensorLocationRelativeToTraffic",
      "type": "text",
      "field_cls": "stripped",
      "multiple": false,
      "use_as_filter": false,
      "ui_widget": "Input",
      "ui_props": {
        "label": "Sensor Location Relative To Traffic",
        "icon": "car",
        "description": "Description of nearby traffic volume or conditions.",
        "required": false,
        "multiple": false
      }
    },
    {
      "name": "sensorcommunity:shortDescriptionOfLocation",
      "type": "text",
      "field_cls": "stripped",
      "multiple": false,
      "use_as_filter": false,
      "ui_widget": "MultiLineInput",
      "ui_props": {
        "label": "Short Description of Location",
        "icon": "align left",
        "description": "Brief description of the sensor location/context.",
        "required": false,
        "multiple": false
      }
    }
  ]
}

Once installed, selecting Sensor.Community in the instrument deposit form loads these community metadata extensions. The section uses the labels and descriptions in the definition.

B2INST Sensor.Community metadata extensions showing location, station, environment, mobility, sensor height, traffic, and location description

Sensor.Community’s Sensor Location Metadata section, corresponding to the definition above. Use Add Line under Location to enter latitude and longitude.

All fields in this definition are optional. Coordinate ranges, non-negative station numbers and heights, and the stated default mobility of stationary appear only in descriptions; the definition does not enforce those ranges or set that default. Choose the mobility value explicitly when it is known.

Values stored in an instrument record#

Community metadata extension values belong inside metadata.community_extension. The selected schema’s UUID belongs in metadata.community_extension_schema. This excerpt uses illustrative values following the declared field types; it is not a complete create or update request, and the screenshot does not contain these values:

{
  "metadata": {
    "community_extension_schema": "<installed-schema-uuid>",
    "community_extension": {
      "sensorcommunity:location": [
        {"latitude": "51.5413", "longitude": "9.9158"}
      ],
      "sensorcommunity:station": 12345,
      "sensorcommunity:environment": "outdoor",
      "sensorcommunity:mobility": "stationary",
      "sensorcommunity:sensorLevelAboveGround": 250,
      "sensorcommunity:sensorLocationRelativeToTraffic": "Residential street with light traffic.",
      "sensorcommunity:shortDescriptionOfLocation": "Sensor mounted on an exterior wall facing a courtyard."
    }
  }
}

Coordinates are strings because their fields use type: "text". Station and sensor height are integers, with height expressed in centimetres. Environment accepts indoor or outdoor; mobility accepts stationary or mobile.

The source definition has one form/value mismatch: shortDescriptionOfLocation is declared as a single text value (multiple: false), but its MultiLineInput widget produces an array of strings and displays Add Line in the screenshot. The excerpt above follows the declared single-value type. Agree a consistent field definition and form widget with the service administrators before relying on the same value shape across form and API submissions.

Replace the schema UUID and illustrative values before use. Keep the namespace in the record keys: do not move these values into top-level custom_fields or the legacy community_specific structure. Preserve the other instrument metadata when updating a draft; see the records API.

For fields backed by a configured vocabulary_id, the backend vocabulary field uses an object containing an id, or a list of such objects when repeated. Explicit options use strings, as in the Sensor.Community environment and mobility fields above. Verify the installed field’s form and API representation before integrating a service vocabulary.

Nested objects and arrays of objects#

A nested field contains a list of child definitions in properties. Children need their own type, ui_widget, and labelled ui_props, just like top-level fields. For MultiNestedInput, the parent also requires ui_props.properties._object_name, which labels each repeated object.

The sensorcommunity:location field in the complete example uses this structure. Its latitude and longitude children are text fields, and _object_name is Location. Setting multiple: true allows more than one location object:

{
  "sensorcommunity:location": [
    {"latitude": "51.5413", "longitude": "9.9158"},
    {"latitude": "51.5420", "longitude": "9.9165"}
  ]
}

The definition and backend field builder support both a single object (multiple: false) and an array (multiple: true), including recursive definitions. The current MultiNestedInput form control always works with an array, however, so it should not be paired with a single-object definition.

The generated nested widget configuration passes child labels and descriptions but does not preserve the child types, options, required flags, or repeating controls. Consequently, the form generally renders children as text inputs. Do not rely on it to provide typed dates, booleans, vocabulary selectors, or arrays inside a nested object. Recursive nested forms are also not implemented by this control. Prefer flat fields or one level of repeated objects with text children, and test the saved values and error display before adopting a nested extension.

Validation#

The extension-definition validator checks supported types, required definition keys, community namespacing, vocabulary configuration, and the nested object label. It does not accept the validators list used in some other schema guides: entries for length, regex, or range are unknown fields in this implementation. There is no conditional-required rule in this definition format.

The field builder can create typed validators, including URL and email fields, but the instrument API currently receives metadata.community_extension as a dictionary of raw values. It does not apply those generated field validators to the extension block during normal record schema validation. The update component filters keys by the selected community’s slug; that is not validation of required values, types, or controlled terms. Clients and reviewers must therefore check the agreed constraints rather than assuming a successful API save proves conformance.

You can inspect an installed definition through:

Request Result
GET /api/community-schema-extension/<community-id-or-slug> The latest schema UUID and a link to its JSON Schema export.
GET /api/community-schema-extension/schema/<schema-uuid> Generated backend, ui, facets, and search_map definitions.
GET /api/community-schema-extension/schema/<schema-uuid>/jsonschema A JSON Schema export wrapped in a response object.

For an existing instrument, inspect the UUID stored in its metadata rather than assuming that it uses the community’s latest schema. See the community schema API for requests.

The JSON Schema export has additional limitations:

  • It removes the community prefix from top-level property names. Its keys are therefore different from the namespaced keys submitted in metadata.community_extension.
  • It does not retain the url or email format constraints of text fields, and dates are exported as date-time even though the field builder uses dates.
  • Nested ui_props.required flags are not converted into required child properties by the normal builder/export path.
  • Configured service vocabularies are not fully represented by the export’s string enumeration; explicit options are the simpler supported case.
  • Repeated fields are exported with uniqueItems: true, while the record API does not itself enforce uniqueness of extension values.

Treat the export as a description that needs these adjustments for client validation, not as an exact schema for an entire API request.

Search and filters#

Extension values are indexed below metadata.community_extension. Query a namespaced field by escaping its colon, for example:

metadata.community_extension.sensorcommunity\:environment:outdoor

This example only matches if the schema is installed, records contain the field, and the caller can access those records. Use the full indexed path; a generated short alias in search_map is not automatically enabled in the search parser. For complete query examples, see advanced search.

The current record mapping allows dynamic extension fields. Date and numeric string detection are disabled, so declaring an extension field as date does not itself install a date index mapping. Use consistent JSON value types and arrange explicit mapping changes with the service administrators if chronological ranges or other typed search behaviour are required. The definition’s nested type also does not automatically create an OpenSearch nested mapping with per-object query semantics.

use_as_filter: true and facet_label let the builder describe a facet for a top-level field. They do not, by themselves, install a working filter in the search interface. The dynamic facet wiring is incomplete, generated paths need to match the actual record mapping, and the generator does not recursively build facets for child fields. Have the service administrators configure and test both the aggregation and its UI filter before documenting it as available to users.

Choosing a maintainable community metadata extension#

Prefer a small set of flat fields with clear labels and controlled options where appropriate. Keep field names and JSON value types stable across schema versions. Changes to a definition do not migrate saved records or existing search mappings. Exercise the actual deposit form and API with valid, missing, and incorrectly typed values before depending on any rule. For requirements outside the capabilities above, discuss the necessary implementation work with the service administrators.

Last update: 06.10.2026

Last review: 06.10.2026