Custom node specification

TypeScript type: CustomNodeSpecification.

Returns a static custom-defined node that's not based on an ECInstance.

Attributes

Name Required? Type Default
Node values
type Yes string
label Yes string
description No string ""
imageId No string ""
Filtering
hideExpression No ECExpression ""
hideIfNoChildren No boolean false
hideNodesInHierarchy No boolean false
suppressSimilarAncestorsCheck No boolean false
Ordering
priority No number 1000
Misc.
hasChildren No "Always" | "Never" | "Unknown" "Unknown"
nestedRules No ChildNodeRule[] []

Attribute: type

Specifies node type, which is assigned to node's key. The type can be used:

The given value is often used in a condition of another child node rule to assign children to this node.

Type string
Is Required Yes
// The ruleset has a root node specification that returns a single custom node with specified parameters. There's
// also a child node rule that assigns the child based on root node's type.
const ruleset: Ruleset = {
  id: "example",
  rules: [
    {
      ruleType: "RootNodes",
      specifications: [
        {
          specType: "CustomNode",
          type: "T_ROOT_NODE",
          label: "My Root Node",
        },
      ],
    },
    {
      ruleType: "ChildNodes",
      condition: `ParentNode.Type = "T_ROOT_NODE"`,
      specifications: [
        {
          specType: "CustomNode",
          type: "T_CHILD_NODE",
          label: "My Child Node",
        },
      ],
    },
  ],
};

Example of using "type" attribute

Attribute: label

Specifies node label. This is a string value that may be localized.

Type string
Is Required Yes
// The ruleset has a root node specification that returns a single custom node with specified parameters.
const ruleset: Ruleset = {
  id: "example",
  rules: [
    {
      ruleType: "RootNodes",
      specifications: [
        {
          specType: "CustomNode",
          type: "T_MY_NODE",
          label: "My Node",
        },
      ],
    },
  ],
};

Example of using "label" attribute

Attribute: description

Specifies the value of Node.description property, which is a string that may be localized. UI component displaying the node may choose whether and how to surface this information to users.

Type string
Is Required No
Default Value ""
// The ruleset has a root node specification that returns a single custom node and assigns it a description.
const ruleset: Ruleset = {
  id: "example",
  rules: [
    {
      ruleType: "RootNodes",
      specifications: [
        {
          specType: "CustomNode",
          type: "T_MY_NODE",
          label: "My Node",
          description: "My node's description",
        },
      ],
    },
  ],
};

Example of using "description" attribute

Attribute: imageId

Specifies node's image ID. If set, the ID is assigned to Node.imageId and it's up to the UI component to decide what to do with it.

Type string
Is Required No
Default Value ""
// The ruleset has a root node specification that returns a single custom node and assigns it an image identifier.
const ruleset: Ruleset = {
  id: "example",
  rules: [
    {
      ruleType: "RootNodes",
      specifications: [
        {
          specType: "CustomNode",
          type: "T_MY_NODE",
          label: "My Node",
          imageId: "my-icon-identifier",
        },
      ],
    },
  ],
};
// Verify that node with correct image identifier is returned
const nodes = await Presentation.presentation.getNodesIterator({ imodel, rulesetOrId: ruleset }).then(async (x) => collect(x.items));
expect(nodes)
  .to.have.lengthOf(1)
  .and.to.containSubset([
    {
      imageId: "my-icon-identifier",
    },
  ]);

Attribute: hideNodesInHierarchy

When true, nodes produced by this specification are omitted and their children appear one hierarchy level higher.

Type boolean
Is Required No
Default Value false
// This ruleset produces a hierarchy that consists of two custom nodes. The parent node is hidden by
// `hideNodesInHierarchy` attribute, thus its child appears one hierarchy level higher.
const ruleset: Ruleset = {
  id: "example",
  rules: [
    {
      ruleType: "RootNodes",
      specifications: [
        {
          specType: "CustomNode",
          type: "parent",
          label: "Parent",
          hideNodesInHierarchy: true,
        },
      ],
    },
    {
      ruleType: "ChildNodes",
      condition: `ParentNode.Type = "parent"`,
      specifications: [
        {
          specType: "CustomNode",
          type: "child",
          label: "Child",
        },
      ],
    },
  ],
};
hideNodesInHierarchy: false hideNodesInHierarchy: true
Example of using "hide nodes in hierarchy" attribute set to "false" Example of using "hide nodes in hierarchy" attribute set to "true"

Attribute: hideIfNoChildren

Specifies whether the node created through this specification should be hidden if it has no child nodes.

Type boolean
Is Required No
Default Value false
// The ruleset contains root node specifications for two custom nodes which are only
// displayed if they have children. One of them has children and the other - not, so
// only one of them is displayed
const ruleset: Ruleset = {
  id: "example",
  rules: [
    {
      ruleType: "RootNodes",
      specifications: [
        {
          specType: "CustomNode",
          type: "2d",
          label: "2d Elements",
          hideIfNoChildren: true,
        },
        {
          specType: "CustomNode",
          type: "3d",
          label: "3d Elements",
          hideIfNoChildren: true,
        },
      ],
    },
    {
      ruleType: "ChildNodes",
      condition: `ParentNode.Type = "2d"`,
      specifications: [
        {
          specType: "InstanceNodesOfSpecificClasses",
          classes: { schemaName: "BisCore", classNames: ["GeometricElement2d"], arePolymorphic: true },
        },
      ],
    },
    {
      ruleType: "ChildNodes",
      condition: `ParentNode.Type = "3d"`,
      specifications: [
        {
          specType: "InstanceNodesOfSpecificClasses",
          classes: { schemaName: "BisCore", classNames: ["GeometricElement3d"], arePolymorphic: true },
        },
      ],
    },
  ],
};
hideIfNoChildren: false hideIfNoChildren: true
Example of using "hide if no children" attribute set to "false" Example of using "hide if no children" attribute set to "true"

Attribute: hideExpression

When specified ECExpression evaluates to true, nodes produced by this specification are omitted and their children appear one hierarchy level higher.

Type ECExpression
Is Required No
Default Value ""
// The ruleset contains root node specifications for two custom nodes which are only
// displayed if they have children. One of them has children and the other - not, so
// only one of them is displayed
const ruleset: Ruleset = {
  id: "example",
  rules: [
    {
      ruleType: "RootNodes",
      specifications: [
        {
          specType: "CustomNode",
          type: "2d",
          label: "2d Elements",
          hideExpression: `ThisNode.HasChildren = FALSE`,
        },
        {
          specType: "CustomNode",
          type: "3d",
          label: "3d Elements",
          hideExpression: `ThisNode.HasChildren = FALSE`,
        },
      ],
    },
    {
      ruleType: "ChildNodes",
      condition: `ParentNode.Type = "2d"`,
      specifications: [
        {
          specType: "InstanceNodesOfSpecificClasses",
          classes: { schemaName: "BisCore", classNames: ["GeometricElement2d"], arePolymorphic: true },
        },
      ],
    },
    {
      ruleType: "ChildNodes",
      condition: `ParentNode.Type = "3d"`,
      specifications: [
        {
          specType: "InstanceNodesOfSpecificClasses",
          classes: { schemaName: "BisCore", classNames: ["GeometricElement3d"], arePolymorphic: true },
        },
      ],
    },
  ],
};
hideExpression evaluates to false hideExpression evaluates to true
Example of using "hide expression" attribute evaluating to "false" Example of using "hide expression" attribute evaluating to "true"

Attribute: suppressSimilarAncestorsCheck

Specifies whether similar ancestor nodes' checking should be suppressed when creating nodes based on this specification. See more in Infinite Hierarchies Prevention page.

Type boolean
Is Required No
Default Value false
// The ruleset contains a root node specification that returns the root `bis.Subject` node. Also, there are two
// child node rules:
// - For any `bis.Model` node, return its contained `bis.Element` nodes.
// - For any `bis.Element` node, return its children `bis.Model` nodes.
// Children of the root `bis.Subject` are all in the single `bis.RepositoryModel` and some of their children are in the same
// `bis.RepositoryModel` as their parent. This means the `bis.RepositoryModel` node has to be repeated in the hierarchy, but
// that wouldn't happen due to duplicate nodes prevention, unless the `suppressSimilarAncestorsCheck` flag is set.
const ruleset: Ruleset = {
  id: "example",
  rules: [
    {
      ruleType: "RootNodes",
      specifications: [
        {
          specType: "InstanceNodesOfSpecificClasses",
          classes: { schemaName: "BisCore", classNames: ["Subject"] },
          instanceFilter: `this.ECInstanceId = 1`,
          groupByClass: false,
          groupByLabel: false,
        },
      ],
    },
    {
      ruleType: "ChildNodes",
      condition: `ParentNode.IsOfClass("Model", "BisCore")`,
      specifications: [
        {
          specType: "RelatedInstanceNodes",
          relationshipPaths: [
            {
              relationship: { schemaName: "BisCore", className: "ModelContainsElements" },
              direction: "Forward",
            },
          ],
          groupByClass: false,
          groupByLabel: false,
        },
      ],
    },
    {
      ruleType: "ChildNodes",
      condition: `ParentNode.IsOfClass("Element", "BisCore")`,
      specifications: [
        {
          specType: "RelatedInstanceNodes",
          relationshipPaths: [
            [
              {
                relationship: { schemaName: "BisCore", className: "ElementOwnsChildElements" },
                direction: "Forward",
              },
              {
                relationship: { schemaName: "BisCore", className: "ModelContainsElements" },
                direction: "Backward",
              },
            ],
          ],
          suppressSimilarAncestorsCheck: true,
          groupByClass: false,
          groupByLabel: false,
        },
      ],
    },
  ],
};

Example of using "suppress similar ancestors check" attribute

Attribute: priority

Controls the order in which specifications are handled — specification with higher priority value is handled first. If priorities are equal, the specifications are handled in the order they appear in the ruleset.

Type number
Is Required No
Default Value 1000
// This ruleset produces a list of `bis.PhysicalModel` and `bis.SpatialCategory` instances and groups them by
// class. "Spatial Category" group will appear first because it has been given a higher priority value.
const ruleset: Ruleset = {
  id: "example",
  rules: [
    {
      ruleType: "RootNodes",
      specifications: [
        {
          specType: "InstanceNodesOfSpecificClasses",
          priority: 1,
          classes: { schemaName: "BisCore", classNames: ["PhysicalModel"], arePolymorphic: true },
        },
        {
          specType: "InstanceNodesOfSpecificClasses",
          priority: 2,
          classes: { schemaName: "BisCore", classNames: ["SpatialCategory"], arePolymorphic: true },
        },
      ],
    },
  ],
};

Example of using "priority" attribute

Attribute: hasChildren

Generally, when a node is created, the rules engine has to determine whether it has children before returning it. This requires evaluating child node rules and, usually, executing additional queries. This attribute allows telling the engine that nodes created by this specification always or never have children, which may substantially improve performance of creating nodes in cases when getting child nodes is expensive.

In case when the attribute value "lies":

  • When set to Always, the returned nodes always have Node.hasChildren set to true. Requesting children for such nodes returns empty list. It's up to the UI component to handle the case of parent node saying it has children but data source not returning any.

  • When set to Never, the returned nodes always have Node.hasChildren set to false. Requesting children for such nodes returns empty list even if there are child node rules that define children for it.

Type "Always" | "Never" | "Unknown"
Is Required No
Default Value "Unknown"
// This ruleset produces a hierarchy of a single root node that hosts a list of `Model` instances. Assuming all
// iModels contain at least one model, the result of this ruleset can be computed quicker by setting
// `hasChildren` attribute to `"Always"`.
const ruleset: Ruleset = {
  id: "example",
  rules: [
    {
      ruleType: "RootNodes",
      specifications: [
        {
          specType: "CustomNode",
          type: "T_ROOT_NODE",
          label: "My Root Node",
          hasChildren: "Always",
        },
      ],
    },
    {
      ruleType: "ChildNodes",
      condition: `ParentNode.Type="T_ROOT_NODE"`,
      specifications: [
        {
          specType: "InstanceNodesOfSpecificClasses",
          classes: [{ schemaName: "BisCore", classNames: ["Model"], arePolymorphic: true }],
        },
      ],
    },
  ],
};
hasChildren: "Always" hasChildren: "Never"
Example of using "has children" attribute set to "always" Example of using "has children" attribute set to "never"

Attribute: nestedRules

Specifications of nested child node rules that allow creating child nodes without the need of supplying a condition to match the parent node.

This is useful when the same instance node at different hierarchy levels needs to have different child nodes. Specifying a child node rule at the root level with condition to match the instance ECClass makes the rule create children for all nodes of that ECClass. When that's not desired, different child node rules may be specified as nested rules for specifications that return instance nodes of the same ECClass - that makes them have different children.

Type ChildNodeRule[]
Is Required No
Default Value []
// The ruleset contains two root nodes' specifications:
// - The first one returns `bis.SpatialCategory` nodes
// - The second one returns `bis.PhysicalModel` nodes and also has a nested child node rule
//   that creates a static "child" node.
// Nested rules apply only to nodes created by the same specification, so the static "child"
// node is created only for the `bis.PhysicalModel`, but not `bis.SpatialCategory`.
const ruleset: Ruleset = {
  id: "example",
  rules: [
    {
      ruleType: "RootNodes",
      specifications: [
        {
          specType: "InstanceNodesOfSpecificClasses",
          classes: [{ schemaName: "BisCore", classNames: ["SpatialCategory"] }],
          groupByClass: false,
        },
        {
          specType: "InstanceNodesOfSpecificClasses",
          classes: [{ schemaName: "BisCore", classNames: ["PhysicalModel"] }],
          groupByClass: false,
          nestedRules: [
            {
              ruleType: "ChildNodes",
              specifications: [
                {
                  specType: "CustomNode",
                  type: "T_CHILD",
                  label: "child",
                },
              ],
            },
          ],
        },
      ],
    },
  ],
};

Example of using "nested rules" attribute

Last Updated: 13 May, 2024