Skip to content

Semantic Filtering API

Max Kasperowski edited this page Sep 4, 2023 · 2 revisions

Semantic Filtering

The semantic filtering API allows for the definition of diagram specific tags and filtering rules that can be used for later filtering and identifying graph elements based on their semantic characteristics. This allows the creation of richer interaction techniques on the client-side rather than relying on purely structural attributes of the different graph elements. Example use cases are filtering of proxy nodes and providing semantic context for structural diagram editing.

API and Usage

Semantic Filtering Tags

SemanticFilterTags can be added to a graph element with the property de.cau.cs.kieler.klighd.semanticFilter.tags. They contain a string that serves to convey semantic information. A tag also acts as an atomic SemanticFilterRule that evaluates to true for an element if that element contains a tag with the same string.

Semantic Filtering Rules

A graph element can contain a list of SemanticFilterRules. Depending on the intended use case it may make sense to attach different rules to different graph elements, but in the use cases we consider here, rules would apply to the entire graph. Therefore, rules are only attached to the root node. It is up to the client feature (e.g. proxy-view) to look for and apply rules on a graph.

Constructing Rules

A SemanticFilterTag, TrueConnective and FalseConnective are atomic rules. Rules can be constructed using connectives and other rules. The following logical connectives are defined:

  • IdentityConnective
  • NegationConnective
  • AndConnective
  • OrConnective
  • IfThenConnective
  • IfThenElseConnective

Numeric Tags and Rules

A SemanticFilterTag can also have a number in addition to a string and there are special numerical connectives that take a SemanticFilterTag as an operand and evaluate to true or false according to the number saved in that tag. The following numeric connectives are defined:

  • NumericEqualConnective
  • NumericNotEqualConnective
  • GreaterThanConnective
  • GreaterEqualsConnective
  • LessThanConnective
  • LessEqualsConnective

Numeric Connectives with Numeric Results

The following rules take numeric inputs and output a numeric value themselves. They can be used to construct more complex expressions such as "states" where the sum of "declarations" and "childCount" is larger than some value.

  • NumericPlusConnective
  • NumericMinusConnective
  • NumericTimesConnective
  • NumericDividesConnective

Code Examples

Adding tags and rules on the server

In the following we declare some simple tags and a numeric tag.

/** Tag giving semantic meaning that the element is a state. */
public static final SemanticFilterTag STATE = new SemanticFilterTag("state");
/** Tag giving semantic meaning that the element is final. */
public static final SemanticFilterTag FINAL = new SemanticFilterTag("final");

/** Returns a tag giving semantic meaning how many declarations an element has. */
public static SemanticFilterTag DECLARATIONS(Double num) {
    return new SemanticFilterTag("numDeclarations", num);
}

/** Returns a tag giving semantic meaning how many declarations an element has without a value.
 * Used for constructing rules involving the tag.
 * /
public static SemanticFilterTag DECLARATIONS = new SemanticFilterTag("numDeclarations);

We can then assign these tags during the synthesis.

node.getProperty(KlighdProperties.SEMANTIC_FILTER_TAGS).add(SCChartsSemanticFilterTags.STATE)
...
node.getProperty(KlighdProperties.SEMANTIC_FILTER_TAGS).add(SCChartsSemanticFilterTags.DECLARATIONS(filteredDeclarations.size as double)) 

Next, we need some rules that we can use to filter elements on the client.in

/** Rule to exclude elements that are states. */
public static final SemanticFilterRule NO_STATES = new NegationConnective(SCChartsSemanticFilterTags.STATE, "Filter States");

/** Rule to only include elements that have at least 3 declarations. */
public static final SemanticFilterRule ONLY_AT_LEAST_3_DECLARATIONS = new OrConnective(
    new LessEqualsConnective(new NumericConstantConnective(3.0), SCChartsSemanticFilterTags.DECLARATIONS
    "Filter Elements With Less Than 3 Declarations");

Finally we can add the rules to the graph (here we add them to the root node) [xtend].

rootNode.setProperty(KlighdProperties.SEMANTIC_FILTER_RULES, #[NO_STATES, ONLY_AT_LEAST_3_DECLARATIONS])

Expression language for creating filter rules

In addition to the programmatic method of defining rules an expression language may be used. SemanticFilterRuleParserUtil.parse(<ExpressionString>) may be used to parse these expressions and obtain the corresponding SemanticFilterRule.

Expressions are built up analogously to the programmatic construction. Each expression is evaluated either to a boolean or numeric value. An atomic expression is either a constant (true, false or a string that can be parsed as Java double) or a tag variable. A tag variable consists of a string identifier (without whitespaces) and is preceded by # to denote a boolean value i.e. the tag is present or not or by a $ to denote a numeric value. Whitespaces are used as separators between operators and expressions. The table below gives an overview over the available operators. Brackets ((,)) may additionally be used to override precedence.

Operator Syntax Input Output Precedence
And <expr> && <expr> boolean boolean 4
Or <expr> || <expr> boolean boolean 3
Not ! <expr> boolean boolean 6
Addition <expr> + <expr> numeric numeric 9
Subtraction <expr> - <expr> numeric numeric 9
Multiplication <expr> * <expr> numeric numeric 10
Division <expr> / <expr> numeric numeric 10
GreaterEquals <expr> >= <expr> numeric boolean 8
GreaterThan <expr> > <expr> numeric boolean 8
LessEquals <expr> <= <expr> numeric boolean 8
LessThan <expr> < <expr> numeric boolean 8
Equals <expr> = <expr> numeric/boolean boolean 7
NotEqual <expr> != <expr> numeric/boolean boolean 7
Valid example expressions
  • #initial || $regions >= 3 - keeps all elements that have the tag initial or have the tag regions with a value of 3 or higher.
  • $children + $importance > $weight - keeps all elements where the sum of the children and importance tags is higher than the value of the weight tag

Evaluating rules on the client

How exactly a feature on the client uses these tags and rules is flexible. In general the rules should be retrieved from the graph and some decision must be made on how to choose a rule based on its name. Then rules can be applied on graph elements, which allows filtering of elements.

Getting filters from the root

import { Filter, getFilters } from "../../filtering/semantic-filtering-util";
...
filters: Filter[] = getFilters(rootNode)

Filter interface

export interface Filter {
    name?: string
    defaultValue?: boolean
    filterFun(el: SKGraphElement): boolean
}

Now using a list of graph elements we can use the filter function to apply this Filter to all graph elements and get a filtered list of graph elements.