Moose Query user documentation
- Introduction
- Exploring a Containment Tree
- Navigating Dependencies (Associations)
- Future plans for MooseQuery
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.
Introduction
Section titled “Introduction”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:
Relations between entities in Moose
Section titled “Relations between entities in Moose”An application model in Moose represents two types of relations:
Containment
Section titled “Containment”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.
Associations between entities
Section titled “Associations between entities”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.
Navigating relations
Section titled “Navigating relations”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.
Exploring a Containment Tree
Section titled “Exploring a Containment Tree”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
Moose Query containment DSL
Section titled “Moose Query containment DSL”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".Containment query options
Section titled “Containment query options”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".Recursive queries
Section titled “Recursive queries”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".Stop condition
Section titled “Stop condition”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".Execute the containment query
Section titled “Execute the containment query”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.Containment queries examples
Section titled “Containment queries examples”Example of a containment tree to use for the next examples:
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}.Containment syntactic sugar
Section titled “Containment syntactic sugar”For the most common use-cases, we added some shortcut methods on TEntityMetalevelDependency:
| Selector | Description | Shortcut for |
|---|---|---|
#containedEntities | Entities directly contained in the receiver | query containedEntities ofAnyType* |
#containers | Direct containers of the receiver | query containers ofAnyType* |
#allContainedEntities | Entities contained in the receiver and entities contained therein, recursively | query containedEntities recursively ofAnyType |
#allContainers | Containers of the receiver and their containers, recursively | query containers recursively ofAnyType |
#containedEntitiesOfType: | Entities of a specific type directly contained in the receiver | query containedEntities ofType: |
#containersOfType: | Direct containers of the receiver of a specific type | query containers ofType: |
#allContainedEntitiesOfType: | Entities of a specific type contained in the receiver and entities of this type contained therein, recursively | query containedEntities recursively ofType: |
#allContainersOfType: | Containers of the receiver of a specific type and their containers of this type, recursively | query 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 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 }"Navigating Dependencies (Associations)
Section titled “Navigating Dependencies (Associations)”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
Moose Query navigation DSL
Section titled “Moose Query navigation DSL”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
Initialize a navigation query
Section titled “Initialize a navigation query”Navigation queries have 3 different messages to initialize them:
entity query has "+ direction".entity query incoming "+ options + execution".entity query outgoing "+ options + execution".#incomingwill 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.#outgoingwill 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.#haswill 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."Navigation query options
Section titled “Navigation query options”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".Get only local dependencies
Section titled “Get only local dependencies”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".Get entities instead of associations
Section titled “Get entities instead of associations”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".Execute the navigation query
Section titled “Execute the navigation query”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 }.Navigation queries examples
Section titled “Navigation queries examples”Example of a navigation model to use for the next examples:
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
Class1givesAttribute1because it is accessed inMethod1that is contained inClass1. 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 }.Navigation syntactic sugar
Section titled “Navigation syntactic sugar”For the most common use-cases, we added some shortcut methods on TEntityMetalevelDependency like for containment queries:
| Selector | Description | Parameters | Shortcut 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 |
#queryAllIncoming | All incoming dependencies. | - | query incoming dependencies |
#queryAllOutgoing | All 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 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:
MooseIncomingQueryResultmanages the result of a query on incoming associations.MooseOutgoingQueryResultmanages the result of a query on outgoing associations.MooseObjectQueryResultcan 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 theobjectoption.
These query result classes are special collections with some new features.
Accessing the opposite entities
Section titled “Accessing the opposite entities”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 }"Excluding internal dependencies
Section titled “Excluding internal dependencies”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`."Navigate the containment tree of the dependencies
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.
Future plans for MooseQuery
Section titled “Future plans for MooseQuery”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.