Skip to content

Moose Query user documentation

Moose-Query is a domain-specific language to build navigation and scope queries for entities in Moose. Its authors are Anne Etien, Jean-Christophe Bach, Vincent Blondeau and Cyril Ferlicot-Delbecque.

This documentation is up-to-date with Moose 13 alpha version of the 6th October 2025.

Moose-Query is an internal domain-specific language (DSL) for creating queries on the relations between entities in Moose. It replaces Moose-Chef, and is designed to simplify and standardize the way to query Moose models.

In order to visualize the concepts, let’s take an example of a simple application model:

parentType
parentType
:Method
:Method
name: methodA1
name: methodA1
signature: methodA1()
signature: methodA1()
LOC: 2
LOC: 2
parentType
parentType
:Attribute
:Attribute
name: attributeA1
name: attributeA1
parentPackage
parentPackage
container
container
:Class
:Class
name: ClassA
name: ClassA
superclass
superclass
subclass
subclass
:Inheritance
:Inheritance
parentPackage
parentPackage
container
container
:Class
:Class
name: ClassB
name: ClassB
:Namespace
:Namespace
name: aNamespace
name: aNamespace
:Package
:Package
name: aPackage
name: aPackage
parentPackage
parentPackage
:Package
:Package
name: anotherPackage
name: anotherPackage
Text is not SVG - cannot display

An application model in Moose represents two types of relations:

It defines which entity contains or are contained in which entities. For example, for a class, it defines the package it is contained in, and the methods and attributes it contains. In the example image, it is the relations parentPackage, container, and parentType. This concept allows one to build the containment tree of a model.

It represents the dependencies between entities. In an application model in Moose, associations are the reification of a dependency. For example, an inheritance is an association between a subclass and its superclass. In the example image, it can be the :Inheritance with the superclass and subclass relations. Another example is that a reference is an association between a behavioral entity and a type.

MooseQuery allows exploring a model via those two concepts. With this tool, it is possible to:

  • Explore the containment tree (the containment DAG, to be more precise): get the entities containing/contained in an entity.
  • Gather associations: get the dependencies of an entity.
  • Manipulate the gathered associations: get the entities that an entity depends or is dependent on
  • Change the scope of entities (move from container to contained entities and vice versa)

Moose Query relies on the model meta-description (that we can access via the method #mooseDescription). The properties of the entities must be meta-described as container, source and target. In order to set those properties, you can check the documentation on.

It is possible to navigate the containment tree of a model easily with Moose Query.

This documentation will be divided into two parts:

  • An explanation of the MooseQuery DSL to create containment queries
  • An explanation of the syntactic sugar we have to cover the most common use-cases of scoping queries

The MooseQuery class is the entry point of the query system of Moose. We are able to query any object using the trait TEntityMetalevelDependency.

Queries should start my sending query to an entity.

entity query "+ containmentQuery + options + execution".

Then they are composed of 3 parts:

  • A message to initialize a containment query
  • A list of options
  • A final message to execute the query

Initialize a containment query by choosing a direction

Section titled “Initialize a containment query by choosing a direction”

To initialize a containment query, two directions are available: container and containedEntities. The direction choice is made by sending either of these messages:

entity query containers "+ options + execution".
entity query containedEntities "+ options + execution".

A containment query can be parameterized to:

  • Gather all containers recursively
  • Gather containers recursively until a condition is matched.
entity query containers recursively "+ execution".
entity query containers recursively until: #isClass "+ execution".

By default, a containment query stops at the first level, i.e. with the first found entities. With the #recursively option, the query gathers the entities contained in the contained entities or the containers of the containers.

entity query containers recursively "+ execution".

When using the recursively option, a stopping condition can be added. The method #until: adds a condition that ends the query when met. This can be used in different ways like speeding up the query by cutting lookup branches or to add some behavior.

For example, to stop the query if the entity were already visited:

entity query containers recursively until: [ :e | self knownDependencies includes: e ] "+ execution".

To execute the query, several triggers exist:

  • #ofAnyType - Any containers. Will select containers regardless of their type.
  • #ofType: - Containers of a specific type. Will select the entities typed according to the type (class or trait) provided as parameter.
  • #ofAnyTpe: - Containers of specific types. Will select the entities typed according to any type (class or trait) in the collection provided as parameter.
  • #withProperty: - Containers matching a property. Will select the entities matching the condition provided as parameter.
entity query containers ofAnyType.
entity query containers ofType: FamixTClass.
entity query containers ofAnyType: { FamixTClass . FamixTNamespace }.
entity query containers withProperty: #hasSourceAnchor.

Example of a containment tree to use for the next examples:

Package1
Package1
Package3
Package3
Namespace1
Namespace1
Class4
Class4
Class1
Class1
Package2
Package2
Class3
Class3
Class2
Class2
Attribute1
Attribute1
Attribute2
Attribute2
Text is not SVG - cannot display

Let’s query:

  • the first package containing Class2:

    class2 query containers ofType: FamixTPackage "=> { package2 }"
  • all children packages and enums in Package1: (even if in this particular case we have no enum inside)

    package1 query containedEntities ofAnyType: { FamixTPackage . FamixJavaEnum } "=> { package2 }"

    In this example, we can see that both a trait (FamixTPackage) and a class(FamixJavaEnum) can be used as types.

  • all the containers recursively of a method:

    attribute2 query containers recursively ofAnyType "=> { class3 . package2 . package1 }"
  • all containers until a condition other than the type of the collected entity is met:

    • containers until a boolean property is true:

      attribute2 query containers withProperty: #isPackage. "=> { package2 }"
    • a class which direct container is a package, so not an inner class:

      attribute2 query containers withProperty: [ :object | object isType and: [ object typeContainer isPackage ] ]. "=> { class3 }"

More random examples:

method := model allModelMethods anyOne.
entity query containers ofType: FamixTNamespace.
entity query containers recursively ofType: FamixTNamespace.
entity query containers recursively ofAnyType: {FamixTNamespace . FamixTClass}.
(entity query containers recursively until: #isClass) ofAnyType: {FamixTNamespace . FamixTClass}.
entity query containedEntities recursively ofType: FamixTNamespace.
(entity query containedEntities recursively until: #isClass) ofAnyType: {FamixTNamespace . FamixTClass}.

For the most common use-cases, we added some shortcut methods on TEntityMetalevelDependency:

SelectorDescriptionShortcut for
#containedEntitiesEntities directly contained in the receiverquery containedEntities ofAnyType*
#containersDirect containers of the receiverquery containers ofAnyType*
#allContainedEntitiesEntities contained in the receiver and entities contained therein, recursivelyquery containedEntities recursively ofAnyType
#allContainersContainers of the receiver and their containers, recursivelyquery containers recursively ofAnyType
#containedEntitiesOfType:Entities of a specific type directly contained in the receiverquery containedEntities ofType:
#containersOfType:Direct containers of the receiver of a specific typequery containers ofType:
#allContainedEntitiesOfType:Entities of a specific type contained in the receiver and entities of this type contained therein, recursivelyquery containedEntities recursively ofType:
#allContainersOfType:Containers of the receiver of a specific type and their containers of this type, recursivelyquery containers recursively ofType:

* The actual implementation is different for optimization reasons.

Examples of containment tree syntactic sugar

Section titled “Examples of containment tree syntactic sugar”

For the following examples, let’s use the same model as the previous example section:

Package1
Package1
Package3
Package3
Namespace1
Namespace1
Class4
Class4
Class1
Class1
Package2
Package2
Class3
Class3
Class2
Class2
Attribute1
Attribute1
Attribute2
Attribute2
Text is not SVG - cannot display
package1 containedEntities. "=> { package2 . class1 }"
class3 containedEntities. "=> { attribute1 . attribute2 }"
package1 allContainedEntities. "=> { package2 . class1 . class2 . class3 . attribute1 . attribute2 }"
class3 allContainedEntities. "=> { attribute1 . attribute2 }"
package1 containers. "=> { }"
class3 containers. "=> { package2 }"
class4 containers. "=> { package3 . namespace1 }"
class3 allContainers. "=> { package2 . package1 }"
attribute1 allContainers. "=> { class3 . package2 . package1 }"

It is possible to navigate the dependencies of a model easily with Moose Query.

This documentation will be divided into two parts on the same schema as the containment queries:

  • An explanation of the MooseQuery DSL to create queries
  • An explanation of the syntactic sugar we have to cover the most common use-cases of navigation queries

The MooseQuery DSL for navigation queries has the same structure as the containment queries DSL. The MooseQuery class is the entry point of the query system of Moose, and we are able to query any object using the trait TEntityMetalevelDependency.

Queries should start my sending query to an entity.

entity query "+ navigationQuery + options + execution"

Then they are composed of 3 parts:

  • A message to initialize a navigation query
  • A list of options
  • A final message to execute the query

Navigation queries have 3 different messages to initialize them:

entity query has "+ direction".
entity query incoming "+ options + execution".
entity query outgoing "+ options + execution".
  • #incoming will query the incoming dependencies of an entity. Incoming dependencies are the entities that depend on the receiver. For example, for a method, the entities using this method.
  • #outgoing will query the outgoing dependencies of an entity. Outgoing dependencies are the entities the receiver depends on. For example, for a method, the entities used in the definition of this method.
  • #has will check if the entity has dependencies. It must be completed with a direction. This query exists only for performance reasons: it stops as soon as a result is found. It is useful, for example, to find statically dead entities.

If you are using #has you should use it this way:

entity query has incoming. "Define that the query will check the existance of incoming dependencies."
entity query has outgoing. "Define that the query will check the existance of outgoing dependencies."

A navigation query can have be parameterized to:

  • Gather only local dependencies
  • Gather the concrete entities at the other side of dependencies, instead of the dependencies themselves.
entity query incoming local "+ execution".
entity query incoming objects "+ execution".

By default, navigation queries return the dependencies of the entity and the dependencies of the entities it contains. For example, for a class, the dependencies of its methods.

The local option restricts the result to the dependencies of the entity without considering those of the entities it contains.

entity query incoming local "+ execution".

By default, the queries will return a collection of associations. For example, for a class, the inheritances coming from subclasses or the inheritance to the superclass.

The objects option will instead return the concrete entities at the other side of these associations. For a class, its subclasses or its superclass.

entity query incoming objects "+ execution".

To execute the query, several triggers exist:

  • #dependencies - Any type of dependency. Will select all dependencies regardless of their type.
  • #dependenciesOfType: - Dependencies of a certain type. Will select the dependencies typed according to the type (class or trait) provided as parameter.
  • #dependenciesOfAnyType: - Dependencies of specific types. Will select the dependencies typed according to any type (class or trait) in the collection provided as parameter.
entity query incoming dependencies.
entity query incoming dependenciesOfType: FamixTReference.
entity query incoming dependenciesOfAnyType: { FamixTReference . FamixTInvocation }.

Example of a navigation model to use for the next examples:

Class1
Class1
parentType
parentType
Method1
Method1
variable(target)
variable(target)
accessor(source)
accessor(source)
Access1
Access1
AttributeA
AttributeA
superclass(target)
superclass(target)
subclass(source)
subclass(source)
Inheritance1
Inheritance1
Class2
Class2
Method2
Method2
target(target)
target(target)
source(source)
source(source)
Reference1
Reference1
Text is not SVG - cannot display

Let’s query:

  • All incoming inheritances of Class1. That is, inheritances from its subclasses:

    class1 query incoming dependenciesOfType: FamixTInheritance. "=> { inheritance1 }"
    class1 query incoming object dependenciesOfType: FamixTInheritance. "=> { class2 }"
  • The incoming inheritances and references of class1. That is, inheritances from its subclasses and the dependencies from the entities referring to it:

    class1 query incoming dependenciesOfAnyType: { FamixTInheritance . FamixTReference }. "=> { inheritance1 . reference1 }"
    class1 query incoming objects dependenciesOfType: FamixTInheritance. "=> { class2 . method2 }"
  • All dependencies to and from Class1:

    class1 query incoming dependencies. "=> { inheritance1 . reference1 }"
    class1 query incoming objects dependencies. "=> { class2 . method2 }"
    class1 query outgoing dependencies. "=> { access1 } This is here because Class1 contains Method1 that access Attribute1 and queries are recursive by default"
    class1 query outgoing objects dependencies. "=> { attribute1 }"
  • Local dependencies to Class1: As we have seen, asking the outgoing dependencies of Class1 gives Attribute1 because it is accessed in Method1 that is contained in Class1. To get only local dependencies:

    class1 query outgoing local dependenciesOfType: FamixTAccess. "=> { }"
    method1 query outgoing local dependenciesOfType: FamixTAccess. "=> {access1 }"

Other examples of local queries:

class1 query incoming local dependencies. "=> { inheritance1 . reference1 }"
class1 query outgoing local dependencies. "=> { }"

More random examples:

method := model allModelMethods anyOne.
entity query incoming dependencies.
entity query incoming dependenciesOfType: FamixTReference.
entity query incoming local dependenciesOfType: FamixTReference.
entity query incoming local object dependenciesOfType: FamixTReference.
entity query incoming local object dependenciesOfAnyType: { FamixTReference . FamixTInvocation }.
entity query outgoing local object dependenciesOfType: FamixTReference.
entity query has incoming local object dependenciesOfAnyType: { FamixTReference . FamixTInvocation }.

For the most common use-cases, we added some shortcut methods on TEntityMetalevelDependency like for containment queries:

SelectorDescriptionParametersShortcut for
#query:with:All the dependencies in a direction, of a specific type.#outor #in.
Expected association type.
query incoming dependenciesOfType: or query outgoing dependenciesOfType:
#queryAll:All the dependencies in a direction.#outor #in.query incoming depenencies or query outgoing dependencies
#queryAllIncomingAll incoming dependencies.-query incoming dependencies
#queryAllOutgoingAll outgoing dependencies.-query outgoing dependencies
#queryIncoming:All incoming dependencies of a specific type.Expected association type.query incoming dependenciesOfType:
#queryOuutgoing:All outgoing dependencies of a specific type.Expected association type.query outgoing dependenciesOfType:

Examples of dependencies navigation syntactic sugar

Section titled “Examples of dependencies navigation syntactic sugar”

For the following examples, let’s use the same model as the previous section:

Class1
Class1
parentType
parentType
Method1
Method1
variable(target)
variable(target)
accessor(source)
accessor(source)
Access1
Access1
AttributeA
AttributeA
superclass(target)
superclass(target)
subclass(source)
subclass(source)
Inheritance1
Inheritance1
Class2
Class2
Method2
Method2
target(target)
target(target)
source(source)
source(source)
Reference1
Reference1
Text is not SVG - cannot display
class1 query: #in with: FamixTInheritance. "=> { inheritance1 }"
class1 query: #out with: FamixTInheritance. "=> { }"
class1 query: #out with: FamixTAccess. "=> { access1 }. Method1 contained in Class1 access to Attribute1 via Access1, result of the query. The access is not directly done via Class1, but via its children."
class1 queryAll: #in. "=> { inheritance1 . reference1 }"
class1 queryAll: #out. "=> { access1 }"
class1 queryAllIncoming. "=> { inheritance1 . reference1 }"
class1 queryAllOutgoing. "=> { access1 } This is here because Class1 contains Method1 that access Attribute1 and queries are recursive by default"
class1 queryIncoming: FamixTInheritance. "=> { inheritance1 }"
class1 queryOutgoing: FamixTInheritance. "=> { }"
class1 queryOutgoing: FamixTAccess. "=> { access1 }. Method1 contained in Class1 access to Attribute1 via Access1, result of the query. The access is not directly done via Class1, but via its children."

Manipulating the gathered results of a navigation query

Section titled “Manipulating the gathered results of a navigation query”

A navigation query returns a result as a MooseQueryResult. There are three types of query results:

  • MooseIncomingQueryResult manages the result of a query on incoming associations.
  • MooseOutgoingQueryResult manages the result of a query on outgoing associations.
  • MooseObjectQueryResult can be obtained via the two others and manages the entities to the other side of the queried associations. For example, for the outgoing accesses of a class, the accessed attributes. It can also be obtained by using the object option.

These query result classes are special collections with some new features.

One of the most useful ones is the #opposites method present on MooseIncomingQueryResult and MooseOutgoingQueryResult. It returns a MooseObjectQueryResult containing all the opposite entities of the query result relative to the receiver of the query.

Indeed, when we query a model, it is often not the associations that we want as result but the source/target entities of these associations. For example, if we want the instance variables accessed by a given method, #query:with: will return the accesses, whereas sending the message #opposites on the result returns the variables themselves.

Taking the previous model as an example we can do:

class1 queryAll: #in. "=> { inheritance1 . reference1 }"
(class1 queryAll: #in) opposites. "=> { class2 . method2 }"

Another possibility is to exclude all the results where the opposite is included in the receiver of the original query. This can be done via the method #withoutSelfLoop. It is handy when we want to query a model to find external dependencies without internal ones.

For the next examples let’s imagine that Attribute1 in our example is contained by Class1.

class1 query outgoing dependencies withoutSelfLoop "=> { } We don't have access1 anymore because it is in `Class1`."
Section titled “Navigate the containment tree of the dependencies”

Another feature is to be able to change the scope of the result:

class1 query outgoing dependencies containersOfType: FamixTClass "=> { class1 }"

This will be the result because the query will target Attribute1 that is contained in Class1.

MooseQuery is evolving to provide users with a simpler, more unified API. This documentation will follow these evolutions.

Currently, Moose query is working tightly with Famix metamodels. Ideally, it should only depend on the meta-metamodel and rely only on meta-information.

Over the years we are reducing those dependencies little by little. The end goal is to have a FameQuery project that could work on any metamodel meta-described by Fame.

In the future, this documentation will be enriched with the description of the API that gives access to the possible types of containers, contained entities, sources and targets of an entity.