Prev Next

The mFQL Language

This section provides a list of Code Miner NBNF Query Language (mFQL) queries with explanations and comments.

The queries shown here demonstrate different capabilities and different approaches to exploring and extracting data using mFQL and the Code Analyzer in Enterprise Architect. The mFQL queries help make the syntax human-readable and intuitive, and have been extended in Enterprise Architect to include additional functions necessary to do real things with Code Miner databases.

The Query Language

String parameters are indicated by string, set parameters are indicated by set and number parameters are indicated by numbers.

Notes

  1. Case sensitivity is defined by the case sensitivity of the language of the source code used to populate the database.  If the source language is case sensitive (such as C++) all string literal parameters are case sensitive. If the source language is case insensitive (such as SQL) all string literal parameters are case insensitive.
  2. Hierarchical traversals in mFQL are generally upwards. Downwards traversals are not optimal, as a node might have any number of child nodes. Upward traversals are much simpler, with every node having zero or one parent node. Downward-looking queries such as 'children' only query one level down.
  3. Synonyms of some keywords are provided to better express a query intent or action in particular circumstances, and to support legacy queries. Synonyms are simple alternatives for the base function keyword. For example, 'type(str)' can be written as 'node(str)' or 'byNode(str)' or 'getByNode(str)'. The current specified version is the preferred one, with the synonyms only intended for use in exceptional circumstances.

Statement

Description

type(value)

type(value)

Extracts a set based upon node name. The exact name for a node is defined by the grammar used to parse the original source. In this example, find all nodes within the database of type "CLASS".

type("CLASS")

Synonyms:

  • node
  • byNode
  • getByNode

with(name)

with(name)

Searches the database for any element that has a named attribute matching the search string. The value of the attribute is ignored - this is a query for the attribute NAME only. All nodes with one or more attributes of the specified name are returned. If a single node has two attributes of the same name, one instance of that node is returned.

This example will find all elements in the database that have an attribute named "Type":

with("Type")

Synonyms:

  • name
  • byName
  • getByName

find(value) find([+] value [+ value] [+])

find(value)

find([+] value [+ value] [+])

Search the database for any element having an attribute value with the provided search term. The match is case sensitive and must match the whole word. You can extract a set based upon an attribute value; when extracting nodes by attribute value, the values of all attributes for the node are considered.

Wildcards allow for specifying a subset of attribute values for a node. Wildcards can be used at either the beginning or end of a value specification:

  • A leading concatenation symbol allows for any number of attributes preceding the first matched attribute
  • A trailing concatenation symbol allows for arbitrary trailing attributes

In both cases, if the node would match without wildcards, it will match with them – the wildcard specifies any number of leading/trailing attributes, including none.

In this example, we retrieve a set of nodes that have their last two attributes being “.” and “sun”. The leading concatenation symbol specifies that any number of attributes (including none), with any value, can exist before the matched attributes, but none can follow.

    find(+ “.” + “sun”)

The next example has a trailing wildcard.  Any node with “com”, “.” and “sun” as the first three attributes will be returned.  Any number of trailing attributes can exist.

     find(“com” + “.” + “sun” +)

Both wildcards can be used together. In this example nodes with attributes with the three specified values as names, in order, regardless of leading or trailing attributes, will be returned.

     find(+ “com” + “.” + “sun” +)

Example: Find all nodes in the database that have any attribute with a value of "CString":

     find("CString")

Example: Find all nodes in the database with a set of attributes having these values in this order:

     find("com" + "." + "sun")

Synonyms:

  • value
  • byValue
  • getByValue

has(name,value)

has(name, value)

Finds all elements that have a named attribute with the value supplied. Unlike the intersection of 'find' and 'with', this query will only return rows with an exact name/value pair.

has("Type","CString")

having(name, value, set)

having (name, value, set)

Finds all elements within the supplied set that have a named attribute with the given value. Similar to 'has' but supplies a predefined input set to search. Whether to use 'has' or 'having' is generally determined by the kind of query structure being used, its depth and its readability.

Example 1: Find all Property elements with a name of "m_strName" that have a Type attribute of CString:

     having("Type","CString",this("PROPERTY","NAME","m_strName"))

Example 2: Extend Example 1 to only include those that store a CString *:

     having("Reference","*",

          having("Type","CString",this("PROPERTY","NAME","m_strName")))

this(type,name,value)

this(type, name, value)

Function finds one or more elements that have a matching TYPE, and WITH a named attribute having the specified VALUE.

Example: Find all operations named "Import Solution":

     this("OPERATION","NAME","ImportSolution")

Synonyms:

  • object
  • item

like(name,like,set)

like(name, like, set)

Finds a set of elements that have an attribute that starts with the search sub-string. Note that this is not a fully wild-carded search but is case sensitive and must be an exact match for the length of the search string.

Example: Find all Classes in the database whose NAME attribute starts with "CMapStr":

     like("NAME","CMapStr",gettype("CLASS"))

and(set1,set2,...)

and(set1, set2, ...)

Returns the intersection of nodes between two or more sets. To be included in the final set, an element must exist in ALL the input sets.

Synonyms:

  • intersect(set, set,...) 
  • {set, set, ...}

union(set1,set2,...)

union(set1,set2, ...)

Returns the distinct union of ALL nodes present in the input sets.

Synonyms:

  • or(set, set ...)
  • [set, set ...]

ancestor(str,set)

ancestor(str, set)

ancestor(num, set)

ancestor(num, str, set)

The ancestor function traverses each node in a set of a number of parent nodes, excluding any nodes that fail the traversal. The number of nodes to traverse, the name of the target node for the traversal, or both can be provided as parameters.

When the number of nodes is provided, but the target node name is not, any nodes with the specified number of parents will pass the traversal. Any node that runs out of parents will be dropped from the set.

When the name of the target is specified, but the number of nodes to traverse is not, any nodes with a parent with a matching name, at any point in the hierarchy, will pass the traversal. Any node with no matching parent is excluded.

When both the number of nodes and the target name are provided, only nodes that have a parent node with the specified name, at the specified offset, pass the traversal. All other nodes are removed from the set.

In this example the set hasParameter("CString","&",1) is moved up to an ancestor node named "OPERATION". If the move fails the node is dropped from the result.

     ancestor("OPERATION",hasParameter("CString","&",1))

In this example the set is moved up one rung to its parent. If there is no parent, the node is dropped from the result.

     ancestor(1,hasParameter("CString","&",1))

In this example the set is moved up three steps to its parent->parent->parent . If there is no such node, the node is dropped from the result.

     ancestor(3,hasParameter("CString","&",1))

Synonyms:

  • move

filter(str,set)

filter(str, set)

filter(num, set)

filter(num, str, set)

The filter function is the same as the 'ancestor' function, except that it returns nodes from the original child set rather than new ancestor nodes. If a node is unable to pass the specified traversal, it is removed from the set. Nodes that pass the traversal are left in place, unmodified.

In this example the set hasParameter("CString","&",1) is tested for an ancestor node named "OPERATION". If the move fails the node is dropped from the result. The result set is a set of parameter types that meet the criteria.

     filter("OPERATION",hasParameter("CString","&",1))

match(NameA,setA,NameB,setB)

match(NameA,setA, NameB,setB)

'Match' takes two input sets and two attribute names and returns all those in 'setA' that have a matching record in 'setB', as determined by comparing the values of the named attributes 'strA' and 'strB'. That is, a 'setA' row is included if the value of attribute 'strA' in 'setA' exists in 'setB' as the value of an attribute of name 'strB'.

'Match' is useful for finding where one element feature is used in a different context elsewhere in the database. For example, where a unique element name or GUID is referenced by another element.

In this example, we match the attribute named 'TYPE' from the right set to the attribute 'NAME' in the left set. The result will be all "CLASS" type objects from the left set with NAME == TYPE(s) as specified in the right set.

     match("NAME",type("CLASS"),"TYPE",this("PROPERTY","NAME","m_pLink"))

graph(targetType, targetName, linkType, linkName, start)

graph(targetType, targetName, linkType, linkName, start)

Find a recursive set of elements that form some kind of graph when linked by attribute pairs, in a manner similar to 'match'. The starter set is queried for all owned instances of the linkType with link Name and these are matched against a new query based on the targetType with targetName. The new set is filtered in a manner similar to 'match', and all elements in the new query that share the same NAME/VALUE pair as from the starter set are kept; all others are discarded. The resultant set is then fed back into the original set as the starter for the next iteration, with the results at each stage being added together to form the final result set.

Example: Return the Class hierarchy for a Class named "Car".

     graph("CLASS","NAME","GENERALIZATION","GENERAL",this("CLASS","NAME","Car"))

prune(set_test,str,set_base)

prune(set_test, str, set_base)

prune(set_test, num, set_base)

For two sets of nodes, temporarily move one set UP to the named or numeric position in its ancestry and filter out any nodes that do not exist by strict intersection in the TEST set. The first set is the TEST set, the right or last set is the BASE set. The set returned is all the elements in the BASE set that, when moved to the TEST position, matched something in the TEST set. The returned nodes are the original nodes from the BASE set and are not moved up when returned.

Example 1 finds the set of parameter types used for operation parameters named "CustomerName" across the whole database.

     prune(this("PARAMETER","NAME","CustomerName"),"PARAMETER",type("PARAMETERTYPE"))

Example 2 finds  all Properties of a Class named Customer, assuming the grammar used to compile the database placed the Property definition two hierarchy levels below the Class definition.

     prune(this("CLASS","NAME","Customer"),2,type("PROPERTY"))

andat(str,test,base)

andat(str, base, test)

andat(num, base, test)

andat(num, str, base, test)

For two sets of nodes, temporarily move one set UP to the named or numeric position in its ancestry and filter out any nodes that do not exist by strict intersection in the TEST set. The first set is the TEST set, the right or last set is the BASE set. The set returned is all the elements in the BASE set that, when moved to the TEST position, matched something in the TEST set. The returned nodes are the original nodes from the BASE set and are not moved up when returned.

Similar to 'prune', this query supports additional options and structures the inputs in a different order to facilitate different kinds of stacked searches.

The 'andat' function performs both a non-destructive tree traversal and an intersect join in one operation. Each node in the left set is traversed according to parameters provided, then the result of the traversal is intersected with the right set. If the intersect passes, the original node is added to the result set. If the intersect fails, the node is excluded from the result set.

The traversal parameters for 'andat' are the same as for 'ancestor' and 'filter'.  For more information about the traversal parameters, see the 'ancestor' function.

Example: For the set of all "PROPERTY" nodes in the database, move them up to a parent node of type CLASS and then intersect the result with the right hand set - in this case a CLASS named CDiagram. All nodes that pass this test are returned as PROPERTY nodes, effectively giving the set of all properties of the Class CDiagram.

     andat("CLASS",type("PROPERTY"),this("CLASS","NAME","CDiagram"))

Synonyms:

  • offsetIntersect
  • offx

unique(left,right) / except(left,right)

unique(left, right)

except(left, right)

Except joins return sets that contain any nodes from either set that do not appear in both sets. This join is similar to a bitwise XOR operation. In set theory, this type of join is referred to as a 'symmetric difference join'.

     {1, 2, 3} excepted with {2, 3, 4} results in {1, 4}

omit(left,right) / exclude(left,right)

omit(left, right)

exclude(left, right)

Exclude joins return a set that contains all nodes from the left set that do not appear in the right set. In set theory, this type of join is referred to as a 'relative complement join'.

     {1, 2, 3} complemented with {2, 3, 4} results in {1}

differ(name,set,name,set)

differ(name, set, name, set)

Return a set of nodes that do not have a matching row in another set, using a NAME/VALUE pair from each set to match on.

Example: This more complex example tests the complete set of Generalizations for a Class hierarchy and identifies missing or unresolved Class names in the total inheritance hierarchy. Like the 'match()' function discussed later, this function iterates over attribute name/value pairs as specified in the left and right input sets, but only includes rows in the final set where there is NO match.

     differ(

       "GENERAL",

       children("GENERALIZATION",

          graph("CLASS","NAME","GENERALIZATION","GENERAL",                                         this("CLASS","NAME","CMainFrame"))),

       "NAME",

               graph("CLASS","NAME","GENERALIZATION","GENERAL",

     this("CLASS","NAME","CMainFrame"))

     )

children(type,set)

children(type, set)

Return a set of child nodes of a specified type for one or more parents in the source set. For all children regardless of type, use an empty string.

For example, in the first query we return ALL first level children of the CMainFrame Class. In the second query we restrict the nodes returned to be of type "REGION" only.

     children("",this("CLASS","NAME","CMainFrame"))

     children("REGION",this("CLASS","NAME","CMainFrame"))

childcount(num,type,set)

childcount (num,type,set)

Return nodes that exactly match the number of specified children of a specified type. For example, only return operations that have 5 parameters.

An example usage is in specifying an exact operation signature, so we check firstly that parameter1 and parameter2 match the type we are querying for, then move those to their operation ancestor and intersect the result with the operation name "GetFromCache" we are interested in. To rule out spurious hits with operations having more than 2 parameters, we explicitly add childcount(2, ...) to ensure we only get operations that have 2 parameters.

     childCount(2,"PARAMETER",

     {

     ancestor("OPERATION",hasParameter("CString","&",1)),

     ancestor("OPERATION",hasParameter("CString","&",2)),

     this("OPERATION","NAME","GetFromCache")

     }

     )

byAddress(num)

byAddress(num)

The byAddress function is used in applying the results of one query to another.  For example, we might have a node of particular interest, and want our query to return only nodes that join (in some way) to the specified node.

     byAddress(node: number)

This example builds a set containing the single node related to the address specified:

     byAddress(11256)

byPosition(File, Offset)

byPosition(File, Offset)

The byPosition function is used to return the inner-most node that covers a certain position in a file. This function is useful for locating a position in the AST based upon a file position.

distinct(set)

distinct(set)

The distinct function ensures that a set has no duplicate values. All duplicate values are excluded from the result set.