- 1 About VIATRA Query Base
- 2 Incremental algorithms
- 3 Getting started
- 4 VIATRA Query Base Programmer documentation
About VIATRA Query Base
VIATRA Query Base aims to provide several useful (some would even argue critical) features for querying EMF models:
- inverse navigation along EReferences
- finding all model elements by attribute value/type (i.e. inverse navigation along EAttributes)
- computing transitive reachability along given reference types (i.e. transitive closure of an EMF model)
- getting all the (direct) instances of a given EClass
The point of VIATRA Query Base is to provide all of these in an incremental way, which means that once the query evaluator is attached to an EMF model, as long as it stays attached, the query results can be retrieved instantly (as the query result cache is automatically updated). VIATRA Query Base is a lightweight, small Java library that can be integrated easily to any EMF-based tool.
We are aware that some of the functionality can be found in some Ecore utility classes (for example ECrossReferenceAdapter). These standard implementations are non-incremental, and are thus do not scale well in scenarios where high query evaluation performance is necessary (such as e.g. on-the-fly well-formedness validation or live view maintenance). VIATRA Query Base has an additional important feature that is not present elsewhere: it contains very efficient implementations of transitive closure that can be used e.g. to maintain reachability regions incrementally, in very large EMF instance models.
The library contains incremental algorithms. In the case of EMF models, an incremental algorithm will visit the whole model only once (when the algorithm is initialized on the model) and after that it will maintain the data structures based on the modifications in the model, without visiting the whole model again. For the transitive closure this will result a remarkable performance speedup; because of the incremental manner of the algorithm, only small portions of a large-scale model will be visited when a modification occurs. Nevertheless, this comes with a price: for large EMF models the incremental algorithms will use additional memory, because the data structures are kept in memory during runtime. The implementation of the algorithms allow to dispose them (and the data structures) any time during the life-cycle.
In the tutorial we will use a simple EMF model which consists of the following elements:
- Library: a library which contains books and writers
- Writer: this element represents a writer who has books in the library
- Book: this element represents a book instance. A book can reference another books via citations.
- Manuscript: this element is a subclass of the Book. It has an additional state attribute which indicates the level of preparedness in percent.
Note that this metamodel is a slight modification of the metamodel which is used in many EMF tutorials (http://www.eclipse.org/edapt/libraryexample.php).
The following figure is the Ecore Diagram of the metamodel.
The required projects are included in VIATRA Query
- VIATRA Query Base from org.eclipse.viatra.query.git (browse, stats, fork on OrionHub)
You will need the following plug-ins:
- org.eclipse.viatra.query.runtime.base: the base plug-in which provides the functionalities mentioned above.
- org.eclipse.viatra.query.runtime.base.itc: this plug-in contains several static and incremental transitive closure algorithm implementation
- org.eclipse..viatra.query.runtime.base.test: this plug-in will be used for the demonstration of the project as it contains all the code that will manipulate a test EMF model and execute the queries. It registers an editor contribution too for the Library Editor.
Follow these steps to run the project:
- You will find a library.genmodel file under the model folder in the org.eclipse.viatra.query.base.test project. Open it and from the context menu choose Generate Model, Edit and Editor Code. This will create 3 plug-ins, named org.eclipse.viatra.query.base.test.library, org.eclipse.viatra.query.base.test.library.edit and org.eclipse.viatra.query.base.test.library.editor.
- Create a new Eclipse Application Run Configuration: go to Run menu -> Run Configurations. Choose Eclipse Application and add a new one.
- Optionally rename the Run Configuration as you like.
- Go to the Plug-ins tab and select Launch with -> plug-ins selected below only.
- Select all the six projects mentioned in this tutorial: the 3 downloaded and the 3 generated in the first step.
- Click on the Add Required Plug-ins button, this will select all the required plug-ins
- Click Apply and then Run. This will start a new Eclipse instance (Runtime Eclipse).
The test project
- Import the Test project in the Runtime Eclipse, the project can be found in the attached zip archive.
- The project contains a model folder and a lib.library instance model in it.
- Open the model with the Library Editor.
- Explore the instance model, you will find a Library instance in it and under the Library there can be found several Writers and Books, Manuscripts.
- You will find a button 'Execute VIATRA Base queries' in the menu above the editor. Please note that this is a simple, demonstration example so this button only works with the sample model in the test project.
You will find several example queries in the org.eclipse.viatra.query.runtime.base.test plug-in's TestModule1.java file (executeQueries method). Note that, the example will print the match results to the STANDARD OUTPUT.
- Query 1 Find all EObjects in the model which have an attribute with the value 100.
- Query 2 Find all Books in the model whose 'pages' attribute's value is 100.
- Query 3 Find all instances of the Book EClass. This will return the Manuscripts too, since Manuscript is a subclass of Book.
- Query 4 Find the proper instances of the Book only, no Manuscripts.
- Query 5 Find all references pointing to the Da Vinci Code book. This will result in the Library instance, Dan Brown Writer and the book Alchemist which cites Da Vinci Code.
- Query 6 Find all Library instances having Da Vinci Code in their 'books' reference list.
- Query 7 Find the inverse references of the Da Vinci Code book but only along the 'books' reference of Library and the 'citation' reference of Book.
Transitive Closure queries (query 8)
We will demonstrate the usage of the TransitiveClosureHelper with the books and their citations in the library.
Suppose we want to borrow a book from the library but we also need all the books which is referred from the selected book via citations, because those may be necessary to understand the selected one (the required books may refer another books which also should be borrowed). This results that we have to know the transitive closure of the graph where the nodes will be the books and each reference between two books will be represented with an edge. To borrow the required books we have to query all the reachable targets of the selected book.
However, if we often go to the library we may query the "target" books many times which will result that we need an algorithm to compute it quickly. In this scenario an incremental algorithm will be really useful which will maintain the required books' citations automatically.
For example if we want to borrow the Angels and Demons book then we don't need to borrow any other books because it is an isolated node in the graph. However, if we want to borrow the Da Vinci Code we will need the Digital Fortress Manuscript, the Faithful Place, Alchemist and the 11 minutes Books too (query 9). Note that Da Vinci Code itself is also present in the result set as the engine allows self-loops.
The following figures illustrates the process of computing the result for the reachable targets of Da Vinci Code.
VIATRA Query Base Programmer documentation
ViatraBaseFactory - integration with EMF models
The library provides the following interfaces:
- NavigationHelper: defines methods to query inverse navigation along EReferences and EAttributes and instances of a EClasses.
- TransitiveClosureHelper: defines methods to query the transitive closure of a given EMF instance model.
Use the ViatraBaseFactory class to create an instance implementing one of the above mentioned interfaces. You must pass a Notifier instance which is the root of the your EMF model. Only the sub model under the given EObject/Resource/ResourceSet root will be visited by the helper instances.
Note that if you do not need an instance later on, call the dispose method of the given interface to properly dispose the data structures of the given algorithm.
The NavigationHelper interface exposes a large set of methods. Consult Javadoc for details.
The Helper can be instantiated either in wildcard mode or in 'parameterized' mode. In wildcard mode, every aspect of the EMF model is automatically indexed. In 'parameterized mode', however, only selected types of model elements and features are indexed. Unused indices take up memory, so turn off wildcard mode to decrease the memory usage while working with very large models.
Unless in wildcard mode, the client has to register EClasses, EDataTypes and EStructuralFeatures before the Helper can index them and find../get... methods can provide the appropriate results. Note that registering new types will cause the Helper to re-iterate through the entire model to expand its index, which is costly, so it is best to register everything you need at once.
The TransitiveClosureHelper interface exposes methods to query reachability information along given EReferences in the EMF model. You must pass a Notifier instance and a set of EReferences when creating a helper instance with the appropriate method of the ViatraBaseFactory class. The set of EReferences represents those instances which will be treated as edges in the 'graph'.
The TransitiveClosureHelper interface extends the ITcDataSource<EObject> interface defined in the org.eclipse.viatra.query.runtime.base.itc project.