1. Path Pattern

Any node in the MTree (Metadata Tree) is uniquely identified by a full path from the root node to that node.

When using paths for node operations, a path pattern can be constructed by using wildcards * and **. A path pattern can represent a set of nodes matching the pattern. Where * denotes a node at one level in the MTree and ** denotes a node at one or more levels.

For example, given time series:

root.sg.d1.s1
root.sg.d1.s2
root.sg.d2.s2
root.sg.group1.d3.s2

Match the corresponding time series by the given path pattern:

  1. root.sg.d1.s1 matches root.sg.d1.s1

  2. root.sg.d1.* matches root.sg.d1.s1、root.sg.d1.s2

  3. root.sg.*.s2 matches root.sg.d1.s2、root.sg.d2.s2

  4. root.sg.**.s2 matches root.sg.d1.s2、root.sg.d2.s2、root.sg.group1.d3.s2

  5. root.sg.d2.** matches root.sg.d2.s2、root.sg.group1.d3.s2

2. Overview of the design

  1. Design path pattern parsing process and the related MTree traversal mechanism to avoid duplicate code

  2. Provide an extensible code framework for different result set queries, and extend the result set processing by overwrite.

3. Logic of Traversal Process

Traverser based on path pattern is the process of comparing node names in MTree with targetName in a given pattern level by level.

In the case of a match between two names, the traverser process will continue; in the case of a mismatch, the nodes in the subsequent levels of the MTree and the pattern will not be compared.

Depending on the targetName, each comparison will be one of the following three cases.

  1. targetName is **, then all children nodes of the current MNode will be processed, because ** may match one or more layers, so the nodes in the subsequent levels of MTree may still match the current **.

  2. targetName is not ** but contains *, then all the children nodes in the current MNode that match the targetName will be processed

  3. targetName does not contain * or **, then the children specified by targetName in the current MNode will be processed.

For the node matching the targetName, a customizable processing interface is provided, with the following two matching cases

  1. internal match: root.sg internal match root.sg.(pattern). A single path search process, the intermediate nodes of the path in some scenarios also need to be processed
  2. full match: root.sg.d full match root.sg.**(pattern). A single path full match node will be processed by a custom method

Parameter description:

  • global parameters
    1. nodes: the targetName list obtained by cutting the given path pattern by separator
    2. isPrefixMatch: whether the current task is a prefix match, default is false, i.e. full path match
  • Recursive parameters
    1. node: the current matched MTree node
    2. idx: the position of the targetName in the path pattern matched by the current node
    3. multiLevelWildcard: whether the node corresponding to the current node is `**` in the path pattern
    4. level: the level of the current node, root is level==0
  /**
   * The recursive method for MTree traversal. If the node matches nodes[idx], then do some
   * operation and traverse the children with nodes[idx+1].
   *
   * @param node current node that match the targetName in given path
   * @param idx the index of targetName in given path
   * @param level the level of current node in MTree
   * @throws MetadataException some result process may throw MetadataException
   */
  protected void traverse(IMNode node, int idx, int level) throws MetadataException {

    if (processMatchedMNode(node, idx, level)) {
      return;
    }

    if (idx >= nodes.length - 1) {
      if (nodes[nodes.length - 1].equals(MULTI_LEVEL_PATH_WILDCARD) || isPrefixMatch) {
        processMultiLevelWildcard(node, idx, level);
      }
      return;
    }

    if (node.isMeasurement()) {
      return;
    }

    String targetName = nodes[idx + 1];
    if (MULTI_LEVEL_PATH_WILDCARD.equals(targetName)) {
      processMultiLevelWildcard(node, idx, level);
    } else if (targetName.contains(ONE_LEVEL_PATH_WILDCARD)) {
      processOneLevelWildcard(node, idx, level);
    } else {
      processNameMatch(node, idx, level);
    }
  }

  /**
   * process curNode that matches the targetName during traversal. there are two cases: 1. internal
   * match: root.sg internal match root.sg.**(pattern) 2. full match: root.sg.d full match
   * root.sg.**(pattern) Both of them are default abstract and should be implemented according
   * concrete tasks.
   *
   * @return whether this branch of recursive traversal should stop; if true, stop
   */
  private boolean processMatchedMNode(IMNode node, int idx, int level) throws MetadataException {
    if (idx < nodes.length - 1) {
      return processInternalMatchedMNode(node, idx, level);
    } else {
      return processFullMatchedMNode(node, idx, level);
    }
  }

3. Detailed Design


4.1 Class Diagram

4.2. Implementation

Class name

Parent

Responsibility

Related upper-level business

MNodeLevelCounter

CounterTraverser

MNode level count

count node

StorageGroupCounter

CounterTraverser

Storage group count

count storage group

EntityCounter

CounterTraverser

Entity count

count device

MeasurementCounter

CounterTraverser

Measurement count

count timeseries


4.3 Extension


The main framework of Traverser is as follows

Method name

Responsibility

protected void traverse(IMNode node, int idx, int level) throws MetadataException

Define the traversal logic of MTree

protected void processMultiLevelWildcard(IMNode node, int idx, int level) throws MetadataException

Define how to handle when targetName is **

protected void processOneLevelWildcard(IMNode node, int idx, int level) throws MetadataException

Define how to handle when targetName is *

protected void processNameMatch(IMNode node, int idx, int level) throws MetadataException

Define how to handle the case where the targetName does not contain a wildcard

If you need to obtain different result sets by traversing MTree, you can extend it by overloading the following functions in Traverser.

Method name

Responsibility

protected abstract boolean processInternalMatchedMNode(IMNode node, int idx, int level)
throws MetadataException;

internal match: root.sg internal match root.sg.**(pattern)
During the traversal, each match of node and targetName provides a processing interface

protected abstract boolean processFullMatchedMNode(IMNode node, int idx, int level)
throws MetadataException;

full match: root.sg.d full match root.sg.**(pattern)
During traversal, provide a processing interface for nodes whose full path matches a pattern


  • No labels