# Map annotations

Ongoing advancements in microscopic techniques, analysis systems, and other areas for which OME attempts to provide metadata exchange require a certain flexibility in the OME model. There is a need to store instrument configuration, script parameters, and similar for later use.

Basic text representations are too difficult to parse to be of significant use. An XML format can be more easily parsed, but a single format would have to be agreed upon by all users. Therefore, it is useful to add particular extension points to the model for which no consensus on a data format has been reached, but where more structure than just text is needed.

A hash map was the most likely candidate for this data; however, rather than limit this to traditional associative arrays, OME maps are defined as a “ordered list of key-value pairs”. The benefit of this representation is that configuration files which are standard for Java `java.util.Properties` objects can be represented fully in a single map. Duplicates are maintained since there is no unique constraint on the list of pairs.

In most cases, the interpretation of the ordered list will be such that the final value for a particular key “wins” as if each value were placed into a hash map in order, with duplicate values replacing previous ones.

## OME-XML

In the OME-XML model, these maps are represented in a compact format. Any map field is defined by the MapPairs complex type which consists of M elements of the form:

    <M K="key">value</M>

## OMERO languages

In OMERO, a slightly more verbose representation of these objects is used. Each map type consists of a list or vector in the respective language, composed of NamedValue objects and possibly nulls.

    // OMERO.java
    ImagingEnvironment environment = new ImagingEnvironmentI();
    environment.setMap(new List<NamedValue>());
    environment.getMap().add(new NamedValue("altitude", "1000m"));
    image.setImagingEnvironment(environment);

### Fields

The concrete fields which are present in the model are currently:

- ExperimenterGroup.config

- GenericExcitationSource.map

- ImagingEnvironment.map

- ImportJob.versionInfo

More will be added as demand increases.

## MapAnnotations

In addition to the fields above, there is also a structured annotation which contains a key-valued pair, the `MapAnnotation`.

    // OMERO.cpp
    MapAnnotation ann = new MapAnnotationI();
    ann->getMapValue().push_back(new NamedValueI("run", "5.0"));
    ann->getMapValue().push_back(new NamedValueI("run", "4.9"));
    ann->getMapValue().push_back(new NamedValueI("run", "5.1"));

This permits the flexible attachment of key-value pairs to any of the OME types which are annotatable. Such annotations attached to key UI elements like images and datasets will be presented by the clients, and can be edited with the appropriate permissions. See the section on Data Annotation in the User guides for more information. See examples of creating MapAnnotations in Java and Python pages.

## Storage and queries

Each map-based field in the OMERO model is represented by an extra table of the form $className_$fieldName. For example, MapAnnotation.mapValue becomes annotation.mapValue, where the loss of “map” from the class name is due to the subclassing of Annotation by MapAnnotation.

In general, use of the specific tables is not necessary and it suffices to write HQL queries based on the classes and field names themselves.

### Find the value for a key

    select nv.value from MapAnnotation ann
      join ann.mapValue as nv
     where nv.name = 'altitude'

### Finding objects with a key

    select ann from MapAnnotation ann
      join ann.mapValue as nv
     where nv.name = 'altitude'

### Finding objects without a key

    select ann from MapAnnotation ann
     where not exists(
        from MapAnnotation m2
        join m2.mapValue as nv2
       where nv2.name like 'size%')

### Finding objects with multiple values

    select ann from MapAnnotation ann
      join ann.mapValue as nv1
      join ann.mapValue as nv2
     where nv1.name = 'date'
       and nv2.name = 'owner'