Select what a diagram shows

A view names the elements its diagram draws, and two statements decide the set: expose collects elements from the model, and filter drops the ones that do not match a condition. Both are SysML v2 constructs, and Syside evaluates them the same way in Modeler and on the command line.

Once the set is settled, Configure diagram views covers the kind of diagram drawn from it and the attributes that shape it.

Collecting elements with expose

Each SysML v2 view must have at least one expose statement. It defines the initial set of elements that are shown in the view. A view can have multiple expose statements, and their results are combined. Once all the expose statements are evaluated, the combined list of elements is passed on to filtering, so getting the initial expose right is crucial.

Note

Unlike other customisation options (such as filter), expose is not inherited. If a view definition declares expose, view usages typed by that definition must redeclare it.

There are multiple ways to write expose statements and each will have a different effect. To make them easier to understand, consider the following simple example model.

package ExposeExample {
    occurrence def SystemContext {
        part rocketShip {
            part firstStage;
            part secondStage;
        }
        part missionControl;
    }

    view systemContextView {
        expose <EXPOSE_PLACEHOLDER>;
    }
}

The following four statements can each replace <EXPOSE_PLACEHOLDER> with a different result:

Syntax

Description

Result

SystemContext

Exposes the namespace itself

SystemContext

SystemContext::*

Exposes the direct children of the namespace

rocketShip, missionControl

SystemContext::**

Exposes the namespace and all its descendants recursively

SystemContext, rocketShip, missionControl, firstStage, secondStage

SystemContext::*::**

Exposes all descendants recursively, but not the namespace itself

rocketShip, missionControl, firstStage, secondStage

Multiple expose statements can be combined to select elements not covered by a single statement. For example, to expose SystemContext, rocketShip, and missionControl without their children:

view systemContextView {
    expose SystemContext;
    expose SystemContext::*;
}

A filter can also be attached directly to an expose statement using square brackets, which is useful when a view has multiple exposes that need different filters. See inline filters in the next section.

Narrowing the set with filter

A filter expression inside a view narrows the exposed elements to only those that match a condition. Filters can test an element’s type using classification operators defined in the KerML specification (see section KerML 7.4.9.2). For example, filter @ SysML::PartUsage checks each exposed element and keeps only those whose type matches PartUsage.

There are four classification operators:

  • @: at least one classification matches (includes subtypes). Returns false on null.

  • istype: all classifications must match (includes subtypes). Returns true on null.

  • hastype: all classifications must match (exact type only, no subtypes). Returns true on null.

  • as (cast): selects values classified by the given type. Useful for accessing metadata attributes in filter expressions.

Consider the following model. It uses the standard library’s RiskMetadata package and a custom FlightCritical metadata tag:

package FilterExample {
    private import RiskMetadata::*;
    metadata def FlightCritical;

    part def DroneSystem {
        part sensor;
        #FlightCritical part controller;
        part camera { @Risk { technicalRisk = RiskLevelEnum::high; } }
        part radio { @Risk { technicalRisk = RiskLevelEnum::medium; } }
        #FlightCritical part battery { @Risk { technicalRisk = RiskLevelEnum::low; } }

        action processData;
        connection dataLink connect sensor to controller;
        allocate processData to controller;
        connect camera to controller;
    }
}

Applying different filters to expose DroneSystem::* produces different results:

Filter

Matches

@ SysML::PartUsage

sensor, controller, camera, radio, battery, dataLink, allocate *, connect *

istype SysML::PartUsage

sensor, dataLink, allocate *, connect *

hastype SysML::PartUsage

sensor

(as Risk).technicalRisk == RiskLevelEnum::high

camera

(as SysML::PartUsage) hastype SysML::PartUsage

sensor, controller, camera, radio, battery

* Unnamed elements created by allocate and connect statements.

The sections below explain why each filter returns what it does.

Matching types and subtypes

filter @ SysML::PartUsage selects every element whose type is PartUsage or any of its specializations. In SysML v2, several usage types specialize PartUsage, including ConnectionUsage and AllocationUsage. This means a view that filters for parts will also include connections and allocations, as shown in the table above.

Note

This is standard SysML v2 behavior, not specific to Syside. The SysML v2 spec (section SysML 7.26.2) uses filter @SysML::PartUsage in its view examples, so users naturally reach for it first. Be aware of the subtype-inclusive semantics.

istype works the same way but with a stricter condition: @ requires at least one of the element’s classifications to match, while istype requires all of them to match. In practice, this means istype fails on elements with metadata annotations (e.g., #FlightCritical part controller), while @ still matches them.

Matching exact types

filter hastype SysML::PartUsage selects only elements whose declared type is exactly PartUsage, excluding specializations. It also fails on elements with metadata annotations, just like istype.

In the model above, only sensor matches because it is the only part with no metadata and no subtype classification.

Warning

Both hastype and istype fail on elements with metadata annotations (e.g., #FlightCritical part controller). Use @ if metadata is present and you don’t need exact type matching.

Accessing metadata attributes

as casts the element to a metadata type, allowing you to access its attributes in filter expressions:

filter (as Risk).technicalRisk == RiskLevelEnum::high;

This casts each element to the Risk metadata type, then checks the technicalRisk attribute. When elements carry multiple metadata annotations, as ensures the attribute is evaluated on the correct type.

For a complete example of metadata-based filtering, see the Filter evaluation example.

Combining operators

To filter for an exact type while still matching elements with metadata, combine as with hastype:

filter (as SysML::PartUsage) hastype SysML::PartUsage;

The as cast isolates the PartUsage classification, and hastype checks that it is exactly PartUsage, not a subtype. This returns all five parts regardless of metadata, while keeping connections and allocations out.

Tip

Start with hastype for precise filtering, then broaden to @ if you need subtypes included.

Inline filters

A filter expression can be attached directly to an expose statement using square brackets, instead of being declared as a separate filter statement. This form is useful when a view has multiple expose statements that need different filters, or when keeping the filter visually adjacent to its source makes the view easier to read.

For a view with a single expose, the two forms produce the same result:

// Separate filter
view V {
    expose MyPackage::*;
    filter hastype SysML::PartUsage;
}

// Inline filter (equivalent for this view)
view V {
    expose MyPackage::*[hastype SysML::PartUsage];
}

The difference shows up when a view has more than one expose. A separate filter declaration applies to every expose in the view, while an inline filter applies only to the expose it is attached to. This makes inline filters the only way to apply different filters to different sources within a single view:

// Separate filter: same condition applied to both exposes
view V1 {
    expose MyPackage::*;      // has part a, part b
    expose MyOtherPackage::*; // has part c, action d
    filter hastype SysML::PartUsage;
}
// Result: part a, part b, part c

// Inline filters: each expose has its own condition
view V2 {
    expose MyPackage::*[hastype SysML::PartUsage];        // has part a, part b
    expose MyOtherPackage::*[hastype SysML::ActionUsage]; // has part c, action d
}
// Result: part a, part b, action d

When to use which form

  • One expose and one filter: either form works. Inline keeps the filter on the same line as the source it applies to, which often reads better.

  • Multiple expose statements that share a filter: use a separate filter declaration. Repeating the same inline filter on each expose is duplication.

  • Multiple expose statements with different filters: inline filters are required; there is no other way to associate a filter with a specific source.

  • Filter expression that references elements being filtered out: see the next section. A separate filter is sometimes safer because it is evaluated outside the exposed scope.

Name resolution inside inline filters

An inline filter expression is resolved in the scope of the elements it filters, not in the scope of the enclosing view. This usually does what you want, since names declared near the source are reachable without imports, but it has one consequence worth calling out: if the filter would hide the element it references, the reference cannot be resolved.

Consider a model that defines metadata in one package and the parts that use it in another:

package ExposeFilterTest {
    package MetadataDefinitions {
        metadata def MyMetadata;
    }

    package MyParts {
        public import MetadataDefinitions::*;
        part partOuter {
            part partInnerWithMetadata { @MyMetadata; }
            part partInnerWithoutMetadata;
        }
        part partOuterWithMetadata { @MyMetadata; }
    }

    part def Views {
        private import MetadataDefinitions::MyMetadata;

        view allPartsWithMetadata {
            // Fails: the filter excludes MyMetadata from the exposed
            // scope, so the unqualified reference cannot resolve.
            expose MyParts::*::**[@MyMetadata];
        }
    }
}

The expose recursively walks MyParts and applies [@MyMetadata] to each element. Because the filter is evaluated against the exposed scope (which includes MyMetadata as an imported member of MyParts), and MyMetadata itself is not annotated with @MyMetadata, the filter excludes it, which leaves the filter’s own reference unresolvable.

The fix is to use a fully qualified name that does not depend on what the filter includes:

view allPartsWithMetadata {
    expose MyParts::*::**[@MetadataDefinitions::MyMetadata];
}

Tip

When in doubt, qualify identifiers in inline filter expressions. A separate filter declaration is evaluated outside the exposed scope and so does not have this restriction, which can be a reason to prefer the separate form for filters that reference metadata or types defined alongside the elements you are filtering.

For a complete example of inline filters in views, see the Filter evaluation example.

Common pitfalls

The following behaviors are correct but regularly surprise view authors.

Interconnection views need part contents

An interconnection view draws features as nodes: part usages with their ports and wiring. A part definition is not a feature, so a directly exposed definition (MyDef::**) is skipped rather than drawn. Expose the definition’s contents (MyDef::*::**) or a part-usage subtree instead. Alternatively, the promoted expose mode keeps the definition’s contents by promoting them to the view frame, prefixed with their origin path.

Some elements cannot be filtered by metadata

Elements like the abstract bases a definition specializes carry no metadata to filter on. Casting with as to a reflective metaclass from the standard library’s SysML package reaches their language-level properties, such as isAbstract:

filter (as SysML::PartDefinition hastype SysML::PartDefinition and
    not (as SysML::Type).isAbstract);

This keeps concrete part definitions and drops the abstract bases they specialize.