=Paper= {{Paper |id=Vol-1305/paper14 |storemode=property |title=Movie Database Case: An EMF-IncQuery Solution |pdfUrl=https://ceur-ws.org/Vol-1305/paper14.pdf |volume=Vol-1305 |dblpUrl=https://dblp.org/rec/conf/staf/SzarnyasSIDHUB14 }} ==Movie Database Case: An EMF-IncQuery Solution== https://ceur-ws.org/Vol-1305/paper14.pdf
        Movie Database Case: An EMF-I NC Q UERY Solution∗

                Gábor Szárnyas        Oszkár Semeráth           Benedek Izsó         Csaba Debreceni
                           Ábel Hegedüs          Zoltán Ujhelyi        Gábor Bergmann
                              Budapest University of Technology and Economics,
                             Department of Measurement and Information Systems,
                              H-1117 Magyar tudósok krt. 2., Budapest, Hungary
         {szarnyas, semerath, izso, debreceni, abel.hegedus, ujhelyiz, bergmann}@mit.bme.hu



       This paper presents a solution for the Movie Database Case of the Transformation Tool Contest 2014,
       using EMF-I NC Q UERY and Xtend for implementing the model transformation.


1     Introduction
Automated model transformations are frequently integrated to modeling environments, requiring both
high performance and a concise programming interface to support software engineers. The objective of
the EMF-I NC Q UERY [2] framework is to provide a declarative way to define queries over EMF models.
EMF-I NC Q UERY extended the pattern language of VIATRA with new features (including transitive
closure, role navigation, match count) and tailored it to EMF models [1].
     EMF-I NC Q UERY is developed with a focus on incremental query evaluation. The latest develop-
ments extend this concept by providing a preliminary rule execution engine to perform transformations.
As the engine is under heavy development, the design of a dedicated rule language (instead of using the
API of the engine) is currently subject to future work. Conceptually, the environment relies on graph
transformation (GT) rules: conditions are specified as EMF-I NC Q UERY patterns, while model manipu-
lation and environment configuration is managed using the Xtend language [3].
     One case study of the 2014 Transformation Tool Contest describes a movie database transforma-
tion [4]. The main characteristics of the transformation related to the application of EMF-I NC Q UERY
are that i) it only adds new elements to the input model (i.e. couple and group are does not modify the
input model), and ii) it is non-incremental (i.e. creating a new group will not affect rule applicability).
     The rest of the paper is structured as follows: Section 2 gives an overview of the implementation,
Section 3 describes the solution including measurement results, and Section 4 concludes our paper.


2     Architecture Overview
The overview of the rule-based solution is illustrated in Figure 1a. The input of the transformation is a
movie model. The result is a transformed movie model including various groups (couples and n-cliques)
and their average rating [4]. The transformation runs in a Java application, that uses pattern matchers
provided by EMF-I NC Q UERY and model manipulation specified in Xtend. The pattern matcher monitors
    ∗ This work was partially supported by the MONDO (EU ICT-611125) and TÁMOP (4.2.2.B-10/1–2010-0009) projects.

This research was realized in the frames of TÁMOP 4.2.4. A/1-11-1-2012-0001 „National Excellence Program – Elaborating
and operating an inland student and researcher personal support system”. The project was subsidized by the European Union
and co-financed by the European Social Fund.

                                                                                  © G. Szárnyas et al.
Submitted to:
                                                                                  This work is licensed under the
TTC 2014
                                                                                  Creative Commons Attribution License.
  2                                                                                        Movie Database Case: An EMF-I NC Q UERY solution



                                                                                                                                                       ContainedElement   children 0..*     Root




                                                                                           <>                    Movie             0..*
                                                                                                MovieType             titleG:GEString      commonMovies
                                                                                            MOVIE                     ratingG:GEDouble
                                                                                            TV                        yearG:GEInt                                           Group
                                                                                            VIDEO                     typeG:GMovieType                               avgRatingG:GEDouble
                                                                                            VIDEOGAME                                                                EAttribute0
                      Java application                                                                               movies   0..*
                               Rule                             Rule                                            persons       0..*
                             condition                      consequence                                                                  p1 0..1
                                                                                                                         Person                                     Couple
                                                                                                                      nameG:GEString
                              Pattern        « notifies »   Rule execution                                                               p2 0..1
                                                                                                                                                                                   Clique
                              matcher                           engine                                                                   0..*
        Movie model
                                                                             Transformed                                                  persons
                                                                             movie model
                                                EMF
                              « monitors »                  « modifies »
                                             resources                                                       Actor                     Actress




                      (a) The specification and runtime                                                                 (b) Our extended metamodel

                                                                       Figure 1: Overview of our approach


  the resources to incrementally update match sets. The application initially reads the input movie database,
  creates the output resources, then executes the transformation, and finally serializes the results into files.
      In the Ecore model of the specification, no containment hierarchy is used and all objects are held in
  the contents list of the EMF resource. However, the performance of the transformation was affected by
  the resource implementation used (since it will determine the implementation of the list operations). To
  avoid this issue, we have extended the metamodel by a Root object (see Figure 1b). This object serves
  as a container for all Group, Movie and Person objects. According to our experiments, this increases the
  speed of the pattern matching by a factor of two. For compatibility with the specification, we ensured that
  our solution works with the models provided and persists outputs in a format without this root element.


  3      Solution
  3.1       Patterns and Transformations
  Task 1: Generating Test Data The synthetic test data is generated in Xtend (see Listing A.2.1). The
  code tightly follows the specification defined in the case description [4].

  Task 2: Finding Couples Couples are listed with the following pattern:
1 pattern pe r so ns T oC ou p le ( p1name , p2name ) {
2   find cast ( p1name , M1 ) ; find cast ( p2name , M1 ) ;
3   find cast ( p1name , M2 ) ; find cast ( p2name , M2 ) ;
4   find cast ( p1name , M3 ) ; find cast ( p2name , M3 ) ;
5   M1 != M2 ; M2 != M3 ; M1 != M3 ;
6   check ( p1name < p2name ) ;
7 }
8 pattern cast ( name , M ) { Movie . persons . name (M , name ) ; }
9 pattern personName (p , pName ) { Person . name (p , pName ) ; }

      Note that the cast pattern returns the names of persons that play in a given movie. This is important
  since the names of the persons can be used to avoid symmetric matches in the personsToCouple pattern
  by sorting. The Couple objects are created and configured in Xtend (see createCouples in line 45 of
  Listing A.2.2). This includes setting the p1 and p2 references using a personName pattern and computing
  the commonMovies by simple set intersection operators (retainAll).
  G. Szárnyas et al.                                                                                                3


  Task 3: Computing Average Rankings The average rankings are computed in Xtend by calculating
  the mean of the rating attributes of a couple’s common movies (see calculateAvgRatings in line 122 of
  Listing A.2.2). The movies are enumerated with the following pattern:
1 pattern c o m m o n M o v i e s O f C o u p l e (c , m ) { Couple . commonMovies (c , m ) ; }



  Extension Task 1: Compute Top-15 Couples This task is mostly implemented in Xtend (see top-
  GroupByRating in line 70 and topGroupByCommonMovies in line 84 of Listing A.2.2), however, it uses
  the groupSize pattern in order to filter the groups with the particular number of members.
1 pattern groupSize ( group , S ) {
2   Group ( group ) ;
3   S == count find memberOfGroup (_ , group ) ;
4 }

     This pattern uses the count find construct which computes the number of matches for a given pattern.
  Additionally, specific comparators are used to sort and determine the top-15 lists by rating or number of
  common movies (see Listing A.2.4).

  Extension Task 2: Finding Cliques The pattern for finding cliques is implemented similarly to the
  personsToCouple pattern 3.1. The pattern for 3-cliques is defined as follows:
1 pattern p e r s o n s T o 3 C l i q u e ( P1 , P2 , P3 ) {
2   find cast ( P1 , M1 ) ; find cast ( P2 , M1 ) ; find cast ( P3 , M1 ) ;
3   find cast ( P1 , M2 ) ; find cast ( P2 , M2 ) ; find cast ( P3 , M2 ) ;
4   find cast ( P1 , M3 ) ; find cast ( P2 , M3 ) ; find cast ( P3 , M3 ) ;
5   M1 != M2 ; M2 != M3 ; M1 != M3 ;
6   check ( P1 < P2 ) ; check ( P2 < P3 ) ;
7   check ( P1 < P3 ) ;
8 }

       The creation of cliques is done similarly to couples (see createCliques in line 138 of Listing A.2.2).
  However, this pattern has a redundant check constraint, as P1 < P2 and P2 < P3 already imply P1 < P3 .
  This works as a hint for the query engine and allows it to filter the permutation of the results (e.g.
  (a2 , a1 , a3 ), (a1 , a3 , a2 ), . . .)) earlier.
       For performance considerations, additional patterns were defined manually for 4- and 5-cliques. For
  larger cliques (n > 5), patterns could be automatically generated using code generation techniques.

  General solution for n-cliques. We also provide the outline for a more general solution (for arbitrary
  n values). For the sake of clarity, we will refer to couples as 2-cliques. In this approach, the cliques are
  built iteratively. Suppose we already have all k-cliques in the graph (e.g. we already added the 2-, 3-, 4-
  and 5-cliques with the previous patterns). To get the (k + 1)-cliques, we look for a group g0 and a person
  p0 that (i) have at least 3 movies in common, (ii) g = g0 ∪ {p0 } is a group that is not a subset of any other
  groups (see Figure 2).
      Formally, (ii) can be expressed as (6 ∃g0 ) : g ⊆ g0 . Using g = g0 ∪ {p0 }, we derive the following
  expression (6 ∃g0 ) : (g0 ⊆ g0 )∧(p0 ∈ g0 ). The g0 ⊆ g0 expression can be formulated as follows: (∀p ∈ g0 ) :
  p ∈ g0 . As the EMF-I NC Q UERY Pattern Language does not have a universal quantifier, we rewrite this
  using the existential quantifier: (6 ∃p ∈ g0 ) : p 6∈ g0 .
      The resulting expression for condition (ii) is the following: (6 ∃g0 ) : ((6 ∃p ∈ g0 ) : p 6∈ g0 ) ∧ (p0 ∈ g0 ).
  We have implemented this general solution (see Listing A.2.3). Pattern subsetOfGroup implements
  condition (ii), while nextClique pattern is capable of determining the (k + 1)-cliques given a model
4                                                    Movie Database Case: An EMF-I NC Q UERY solution


                                        m        m    m    m    m




                                            g0        p0

                                                 g


             Figure 2: Matching 3-clique groups in the positive test pattern. g0 is a couple.


containing all k-cliques. This approach is functionally correct, however, it only works for very small
input models and hence is omitted from our measurements.

Extension Task 3: The average rankings are computed the same way as in task 3.

Extension Task 4: The top 15 average rankings are computed the same way as in extension task 2.

3.2   Optimizations
To increase the performance of the transformations, we carried out some optimizations. (1) The com-
mon movies of the two Person objects are computed from Xtend instead of EMF-I NC Q UERY. (2) The
patterns for 3-, 4- and 5-cliques are implemented manually. (3) Common subpatterns were identified and
extracted them into separate patterns, as the engine can reuse the pattern for each occurrence, and makes
the query definition file easier to maintain. For an example, see the cast pattern in A.1.

3.3   Benchmark Results
The implementation was benchmarked in the SHARE cloud, on an Ubuntu 12.04 64-bit operating system
running in a VirtualBox environment. The virtual machine used one core of an Intel Xeon E5-2650 CPU
and had 6 GB of RAM. The transformations were ran in a timeout window of 10 minutes.

3.4   Synthetic model
Results are displayed in Figure 3. The diagram shows the transformation times for creating couples and
cliques for synthetic models. The results show that the transformations run in near linear time.
     The dominating factor of the running time is the initialization of the query engine. However, after
initialization, creating groups can be carried out efficiently. Furthermore, our experiments showed that
the limiting factor for our solution is the memory consumption of the incremental query engine. Given
more memory, the solution is capable of transforming larger models as well.

3.5   IMDb model
In the given time range and memory constraints, the transformation of the IMDb model could only
generate the couples and 3-cliques for the smallest instance model. Finding the couples took 3 minutes,
G. Szárnyas et al.                                                                                                            5


                                                          Runtime of Transformation Tasks
                                 100

                                  90

                                  80

                                  70

                                  60




                      Time (s)
                                  50

                                  40

                                  30

                                  20

                                  10

                                   0
                                       0   1000   2000   3000    4000       5000      6000    7000      8000   9000   10000
                                                                           Size (N)

                                                     Couples    3-Clique        4-Clique     5-Clique



                                                   Figure 3: Benchmark results


while finding 3-cliques took 6. However, in case of a live and evolving model, our solution is capable of
incrementally running the transformation which in practice results in near instantaneous response time.

3.6    Transformation Correctness and Reproducibility
Our solution was developed as Eclipse plug-ins, however, it is also available as a command line applica-
tion compiled using the Apache Maven.The transformation runs correctly for the provided test cases on
SHARE1 , and the source code is also available in a Git repository2 . The results of the transformations
were spot-checked for both synthetic and IMDb models.


4     Conclusion
In this paper we have presented our implementation of the Movie Database Case. The solution uses
EMF-I NC Q UERY as a model query engine: the transformation is specified using declarative graph pat-
tern queries over EMF models for rule preconditions, and Xtend code for model manipulations. The
main conclusion of the performance evaluation is that EMF-IncQuery’s incremental approach is not a
good fit for this case study as the transformation is very model manipulation dominant.


References
[1] Gábor Bergmann, Zoltán Ujhelyi, István Ráth & Dániel Varró (2011): A Graph Query Language for EMF
    models. In: Theory and Practice of Model Transformations, Fourth Int. Conf., LNCS 6707, Springer.
[2] Eclipse.org (2014): EMF-IncQuery. http://eclipse.org/incquery/.
[3] Eclipse.org (2014): Xtend – Modernized Java. https://www.eclipse.org/xtend/.
[4] Matthias Tichy Tassilo Horn, Christian Krause (2014): The TTC 2014 Movie Database Case. In: 7th Trans-
    formation Tool Contest (TTC 2014), EPTCS.
    1 http://is.ieis.tue.nl/staff/pvgorp/share/?page=ConfigureNewSession&vdi=Ubuntu12LTS_TTC14_

64bit_TTC14-EIQ-imdb.vdi
   2 https://git.inf.mit.bme.hu/w?p=projects/viatra/ttc14-eiq.git (username: anonymous, no password).
   6                                                Movie Database Case: An EMF-I NC Q UERY solution


   A     Appendix – Movie Database Case Transformation Code
   A.1   EMF-I NC Q UERY Graph Patterns

 1 package hu . bme . mit . ttc . imdb . queries
 2
 3 import " http :// movies /1.0 "
 4
 5
 6 // Shorthand patterns
 7 pattern personName (p , pName ) {
 8    Person . name (p , pName ) ;
 9 }
10
11 // Actor with name is part of the case of movie M
12 pattern cast ( name , M ) {
13    Movie . persons . name (M , name ) ;
14 }
15
16 // Movie m is a common movie of Couple c
17 pattern c o m m o n M o v i e s O f C o u p l e (c , m ) {
18    Couple . commonMovies (c , m ) ;
19 }
20
21 /* *
22 * This pattern determines if a person is a member of a group .
23 */
24 pattern memberOfGroup ( person , group ) {
25    Couple . p1 ( group , person ) ;
26 } or {
27    Couple . p2 ( group , person ) ;
28 } or {
29    Clique . persons ( group , person ) ;
30 }
31
32 /* *
33 * This pattern determines the size of a group .
34 */
35 pattern groupSize ( group , S ) {
36    Group ( group ) ;
37    S == count find memberOfGroup (_ , group ) ;
38 }
39
40 // Couple patterns
41 /* *
42 * This pattern looks for two person names ( p1name , p2name ) , who were in the cast of
43 * three different movies ( M1 , M2 , M3 ) .
44 * The names are ordered l e x i c o g r a p h i c a l l y in order to list the same pair only one
45 * ( the match set contains only {( a1 , a2 ) } instead of {( a1 , a2 ) , ( a2 , a1 ) }.
46 */
47 pattern pe r so ns T oC ou p le ( p1name , p2name ) {
48    find cast ( p1name , M1 ) ; find cast ( p2name , M1 ) ;
49    find cast ( p1name , M2 ) ; find cast ( p2name , M2 ) ;
50    find cast ( p1name , M3 ) ; find cast ( p2name , M3 ) ;
51
52    M1 != M2 ; M2 != M3 ; M1 != M3 ;
53
54    check ( p1name < p2name ) ;
55 }
56
57 /* *
58 * This pattern looks for the common movies of a couple .
59 * The couple is determined with the pe rs o ns T oC ou p le pattern .
60 */
    G. Szárnyas et al.                                                                                                      7


 61 pattern c o m m o n M o v i e s T o C o u p l e ( p1name , p2name , m ) {
 62    find pe r so ns T oC ou p le ( p1name , p2name ) ;
 63
 64    Person . movies ( p1 , m ) ;
 65    Person . movies ( p2 , m ) ;
 66    Person . name ( p1 , p1name ) ;
 67    Person . name ( p2 , p2name ) ;
 68
 69    check ( p1name < p2name ) ;
 70 }
 71
 72 /* *
 73 * Returns with the number of common movies of a couple .
 74 */
 75 pattern c o u n t O f C o m m o n M o v i e s O f C o u p l e ( p1 , p2 , n ) {
 76    Couple . p1 (c , p1 ) ;
 77    Couple . p2 (c , p2 ) ;
 78    n == count find c o m m o n M o v i e s O f C o u p l e (c , _m ) ;
 79 }
 80
 81 // Clique patterns
 82 /* *
 83 * Similarly to the couple pattern , this pattern looks for                        3 - cliques .
 84 */
 85 pattern p e r s o n s T o 3 C l i q u e ( P1 , P2 , P3 ) {
 86    find cast ( P1 , M1 ) ; find cast ( P2 , M1 ) ; find cast ( P3 , M1 ) ;
 87    find cast ( P1 , M2 ) ; find cast ( P2 , M2 ) ; find cast ( P3 , M2 ) ;
 88    find cast ( P1 , M3 ) ; find cast ( P2 , M3 ) ; find cast ( P3 , M3 ) ;
 89
 90    M1 != M2 ; M2 != M3 ; M1 != M3 ;
 91
 92    check ( P1 < P2 ) ; check ( P2 < P3 ) ;
 93 }
 94
 95 /* *
 96 * Similarly to the couple pattern , this pattern looks for                        4 - cliques .
 97 */
 98 pattern p e r s o n s T o 4 C l i q u e ( P1 , P2 , P3 , P4 ) {
 99    find cast ( P1 , M1 ) ; find cast ( P2 , M1 ) ; find cast ( P3 , M1 ) ;        find cast ( P4 , M1 ) ;
100    find cast ( P1 , M2 ) ; find cast ( P2 , M2 ) ; find cast ( P3 , M2 ) ;        find cast ( P4 , M2 ) ;
101    find cast ( P1 , M3 ) ; find cast ( P2 , M3 ) ; find cast ( P3 , M3 ) ;        find cast ( P4 , M3 ) ;
102
103    M1 != M2 ; M2 != M3 ; M1 != M3 ;
104
105    check ( P1 < P2 ) ; check ( P2 < P3 ) ; check ( P3 < P4 ) ;
106 }
107
108 /* *
109 * Similarly to the couple pattern , this pattern looks for                        5 - cliques .
110 */
111 pattern p e r s o n s T o 5 C l i q u e ( P1 , P2 , P3 , P4 , P5 ) {
112    find cast ( P1 , M1 ) ; find cast ( P2 , M1 ) ; find cast ( P3 , M1 ) ;        find cast ( P4 , M1 ) ; find cast (
           P5 , M1 ) ;
113    find cast ( P1 , M2 ) ; find cast ( P2 , M2 ) ; find cast ( P3 , M2 ) ;        find cast ( P4 , M2 ) ; find cast (
           P5 , M2 ) ;
114    find cast ( P1 , M3 ) ; find cast ( P2 , M3 ) ; find cast ( P3 , M3 ) ;        find cast ( P4 , M3 ) ; find cast (
           P5 , M3 ) ;
115
116    M1 != M2 ; M2 != M3 ; M1 != M3 ;
117
118    check ( P1 < P2 ) ; check ( P2 < P3 ) ; check ( P3 < P4 ) ; check ( P4         < P5 ) ;
119 }
     8                                                                 Movie Database Case: An EMF-I NC Q UERY solution


     A.2       Xtend Code
     A.2.1         Generator Code
1 /* *
2  * This class implements the test model generator logic .
3  */
4 class Generator {
5
6        // The EMF resource on which the tr ansfor mation operates
7        public Resource r
8
9        // We define this extension to help with model element creation
10       extension MoviesFactory = MoviesFactory . eINSTANCE
11
12       // method to generate an example of size N
13       def generate ( int N ) {
14         createExample ( N ) ;
15       }
16
17       // create N test cases in the model
18       def createExample ( int N ) {
19         (0 .. N - 1) . forEach [ createTest ( it ) ]
20       }
21
22       // create a test cases in the model with parameter n
23       def createTest ( int n ) {
24         crea tePosi tive ( n )
25         crea teNega tive ( n )
26       }
27
28       // create a positive match for the test case
29       // initialize some movies and actors / actresses
30         // create i n t e r c o n n e c t i o n s according to a logic that will yield a positive match
31       def create Positi ve ( int n ) {
32         val movies = newArrayList ()
33         (0 .. 4) . forEach [ movies += createMovie (10 * n + it ) ]
34
35           val    a   =   createActor ( " a "   +   (10   *   n))
36           val    b   =   createActor ( " a "   +   (10   *   n +   1) )
37           val    c   =   createActor ( " a "   +   (10   *   n +   2) )
38           val    d   = createActress ( " a "   +   (10   *   n +   3) )
39           val    e   = createActress ( " a "   +   (10   *   n +   4) )
40
41           val actors =   #[ a , b , c , d , e ]
42           val firstTwo = #[ a , b ]
43           val lastTwo = #[              d, e]
44
45           movies . get (0) . persons += firstTwo ;
46           (1 .. 3) . forEach [ movies . get ( it ) . persons += actors ]
47           movies . get (4) . persons += lastTwo
48
49           r . contents += actors
50           r . contents += movies
51       }
52
53       // create a positive match for the test case
54       // initialize some movies and actors / actresses
55         // create i n t e r c o n n e c t i o n s according to a logic that will yield a negative match
56       def create Negati ve ( int n ) {
57         val movies = newArrayList ()
58         (5 .. 9) . forEach [ movies += createMovie (10 * n + it ) ]
59
60           val a =        createActor ( " a " + (10 * n + 5) )
61           val b =        createActor ( " a " + (10 * n + 6) )
     G. Szárnyas et al.                                                  9


62           val c = createActress ( " a " + (10 * n + 7) )
63           val d = createActress ( " a " + (10 * n + 8) )
64           val e = createActress ( " a " + (10 * n + 9) )
65
66           val actors =                        #[ a ,   b, c, d, e]
67           movies . get (0) . persons     +=   #[ a ,   b]
68           movies . get (1) . persons     +=   #[ a ,   b, c]
69           movies . get (2) . persons     +=   #[       b, c, d]
70           movies . get (3) . persons     +=   #[          c, d, e]
71           movies . get (4) . persons     +=   #[             d, e]
72
73           r . contents += actors
74           r . contents += movies
75       }
76
77       // create a movie with the given rating
78       def createMovie ( int rating ) {
79         val movie = createMovie
80         movie . rating = rating
81         movie
82       }
83
84       // create an actor with the given name
85       def createActor ( String name ) {
86         val actor = createActor
87         actor . name = name
88         actor
89       }
90
91       // create an actress with the given name
92       def createActress ( String name ) {
93         val actress = createActress
94         actress . name = name
95         actress
96       }
97
98   }

     A.2.2      Transformation Code

1 /* *
2  * This class implements the t ransfo rmatio n logic .
3  */
4 class Tr ansfor mation {
5
6        /* *
7          * Initialize the transf ormati on processor on a resource .
8          * The runtime of the trans format ion steps are logged .
9          * @param r The target resource of the tr ansfor mation .
10         * @param bmr The benchmark logger .
11         */
12       new ( Resource r , B e n c h m a r k R e s u l t s bmr ) {
13          this . r = r ;
14          this . bmr = bmr ;
15          this . root = r . contents . get (0) as Root
16       }
17
18       // to store the benchmark results
19       protected val B e n c h m a r k R e s u l t s bmr ;
20       // to store the model
21       protected Resource r
22
23       // //// Resources Management
24       protected val Root root ;
25       /* *
     10                                                                        Movie Database Case: An EMF-I NC Q UERY solution


26      * Helper function to add elements to the target resource .
27      * @param
28      */
29    def a d d E l e m e n t T o R e s o u r c e ( C o n t a i n e d E l e m e n t c o n t a i n e d E l e m e n t ) {
30       root . children . add ( c o n t a i n e d E l e m e n t )
31    }
32    def a d d E l e m e n t s T o R e s o u r c e ( Collection  c o n t a i n e d E l e m e n t s ) {
33       root . children . addAll ( c o n t a i n e d E l e m e n t s )
34    }
35    def g e t E l e m e n t s F r o m R e s o u r c e () {
36       root . children
37    }
38    // / / / / / / / / / / / / / / / / / / / / / / / / / /
39
40    // to help with model manipulation
41    extension MoviesFactory = MoviesFactory . eINSTANCE
42    extension Imdb = Imdb . instance
43
44    // create couples
45    public def createCouples () {
46      val engine = A d v a n c e d I n c Q u e r y E n g i n e . c r e a t e U n m a n a g e d E n g i n e ( r )
47      val coupleMatcher = engine . p er so n sT oC o up l e
48      val c o m m o n M o v i e s M a t c h e r = engine . c o m m o n M o v i e s T o C o u p l e
49      val p e r s o n N a m e M a t c h e r = engine . personName
50
51        val newCouples = new LinkedList < Couple >
52        coupleMatcher . forEachMatch [
53          val couple = createCouple ()
54          val p1 = p e r s o n N a m e M a t c h e r . g et Al l Va lu e sO fp ( p1name ) . head
55          val p2 = p e r s o n N a m e M a t c h e r . g et Al l Va lu e sO fp ( p2name ) . head
56          couple . setP1 ( p1 )
57          couple . setP2 ( p2 )
58          val commonMovies = c o m m o n M o v i e s M a t c h e r . g et Al l Va lu e sO fm ( p1name , p2name )
59          couple . commonMovies . addAll ( commonMovies )
60
61            newCouples += couple
62        ]
63
64        println ( " # of couples = " + newCouples . size )
65        engine . dispose
66        a d d E l e m e n t s T o R e s o u r c e ( newCouples ) ;
67    }
68
69    // calculate the top group by rating
70    def t o p G r o u p B y R a t i n g ( int size ) {
71      println ( " Top -15 by Average Rating " )
72      println ( " = = = = = = = = = = = = = = = = = = = = = = = = " )
73      val n = 15;
74
75        val engine = Inc QueryE ngine . on ( r )
76        val c o u p l e W i t h R a t i n g M a t c h e r = engine . groupSize
77        val rankedCouples = c o u p l e W i t h R a t i n g M a t c h e r . g e t A l l V a l u e s O f g r o u p ( size ) . sort (
78          new G r o u p A V G C o m p a r a t o r )
79
80        printCouples (n , rankedCouples )
81    }
82
83    // calculate the top group by common movies
84    def t o p G r o u p B y C o m m o n M o v i e s ( int size ) {
85      println ( " Top -15 by Number of Common Movies " )
86      println ( " = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = " )
87
88        val n = 15;
89        val engine = Inc QueryE ngine . on ( r )
90        val c o u p l e W i t h R a t i n g M a t c h e r = engine . groupSize
      G. Szárnyas et al.                                                                                                                          11


 91
 92         val rankedCouples = c o u p l e W i t h R a t i n g M a t c h e r . g e t A l l V a l u e s O f g r o u p ( size ) . sort (
 93           new G r o u p S i z e C o m p a r a t o r
 94         )
 95         printCouples (n , rankedCouples )
 96     }
 97
 98     // pretty - print couples
 99     def printCouples ( int n , List < Group > rankedCouples ) {
100       (0 .. n - 1) . forEach [
101         if ( it < rankedCouples . size ) {
102            val c = rankedCouples . get ( it ) ;
103            println ( c . printGroup ( it ) )
104         }
105       ]
106     }
107
108     // pretty - print groups
109     def printGroup ( Group group , int lineNumber ) {
110       if ( group instanceof Couple ) {
111          val couple = group as Couple
112          return ’’’« lineNumber » . Couple avgRating « group . avgRating » , « group . commonMovies .
                  size » movies ( « couple . p1 . name » ; « couple . p2 . name » ) ’’’
113       }
114       else {
115          val clique = group as Clique
116          return ’’’« lineNumber » . « clique . persons . size » - Clique avgRating « group . avgRating » , «
                  group . commonMovies . size » movies ( «
117             FOR person : clique . persons SEPARATOR " , " » « person . name » « ENDFOR » ) ’’’
118       }
119     }
120
121     // calculate average ratings
122     def c a l c u l a t e A v g R a t i n g s () {
123       g e t E l e m e n t s F r o m R e s o u r c e . filter ( typeof ( Group ) ) . forEach [ x | c a l c u l a t e A v g R a t i n g ( x .
                  commonMovies , x ) ]
124     }
125
126     // calculate average rating
127     protected def c a l c u l a t e A v g R a t i n g ( Collection < Movie > commonMovies , Group group ) {
128       var sumRating = 0.0
129
130         for ( m : commonMovies ) {
131           sumRating = sumRating + m . rating
132         }
133         val n = commonMovies . size
134         group . avgRating = sumRating / n
135     }
136
137     // create cliques
138     public def createCliques ( int cliques ) {
139       val engine = A d v a n c e d I n c Q u e r y E n g i n e . c r e a t e U n m a n a g e d E n g i n e ( r )
140       val personMatcher = getPersonName ( engine )
141       var Collection < Clique > newCliques
142
143         if ( cliques == 3) {
144            val clique3 = g e t P e r s o n s T o 3 C l i q u e ( engine )
145
146            newCliques = clique3 . allMatches . map [ x | ge nerate Clique (
147              personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p1 ) .p ,
148              personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p2 ) .p ,
149              personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p3 ) . p ) ]. toList ;
150         }
151         else if ( cliques == 4) {
152           val clique4 = g e t P e r s o n s T o 4 C l i q u e ( engine )
      12                                                                   Movie Database Case: An EMF-I NC Q UERY solution


153
154              newCliques = clique4 . allMatches . map [ x | ge nerate Clique (
155                personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p1 ) .p ,
156                personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p2 ) .p ,
157                personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p3 ) .p ,
158                personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p4 ) . p ) ]. toList ;
159           }
160           else if ( cliques == 5) {
161             val clique5 = g e t P e r s o n s T o 5 C l i q u e ( engine )
162             newCliques = clique5 . allMatches . map [ x | ge nerate Clique (
163               personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p1 ) .p ,
164               personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p2 ) .p ,
165               personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p3 ) .p ,
166               personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p4 ) .p ,
167               personMatcher . g e t O n e A r b i t r a r y M a t c h ( null , x . p5 ) . p ) ]. toList ;
168           }
169
170           println ( " # of " + cliques + " - cliques = " + newCliques . size )
171
172           engine . dispose
173           newCliques . forEach [ x | x . commonMovies . addAll ( x . c o l l e c t C o m m o n M o v i e s ) ]
174           a d d E l e m e n t s T o R e s o u r c e ( newCliques ) ;
175       }
176
177       // generate cliques
178       protected def ge nerate Clique ( Person ... persons ) {
179         val c = createClique
180         c . persons += persons
181         return c
182       }
183
184       // collect common movies
185       protected def c o l l e c t C o m m o n M o v i e s ( Clique clique ) {
186         var Set < Movie > commonMovies = null ;
187         for ( personMovies : clique . persons . map [ movies ]) {
188           if ( commonMovies == null ) {
189               commonMovies = personMovies . toSet ;
190           }
191           else {
192               commonMovies . retainAll ( personMovies )
193           }
194         }
195         return commonMovies
196       }
197   }

      A.2.3       General Clique Patterns
      The resulting expression for condition (ii) is the following: (6 ∃g0 ) : ((6 ∃p0 ∈ g0 ) : p0 6∈ g0 ) ∧ (p ∈ g0 ).
      This is equivalent to the following EMF-I NC Q UERY pattern:
 1 /* * Group g0 is a subset of Group gx . */
 2 pattern subsetOfGroup ( g0 : Group , gx : Group ) {
 3    neg find n o t S u b s e t O f G r o u p ( p0 , g0 , gx ) ;
 4 }
 5
 6 /* * This pattern returns is a helper for the subsetOfGroup pattern . */
 7 pattern n o t S u b s e t O f G r o u p ( p0 : Person , g0 : Group , gx : Group ) {
 8    find memberOfGroup ( p0 , g0 ) ;
 9    neg find memberOfGroup ( p0 , gx ) ;
10 }
11
12 /* * Person p is a member of Group g . A Group is either a Couple or a Clique . */
13 pattern memberOfGroup (p , g ) {
14    Couple . p1 (g , p ) ;
     G. Szárnyas et al.                                                                                   13


15 }     or {
16       Couple . p2 (g , p ) ;
17 }     or {
18       Clique . persons (g , p ) ;
19 }

          Based on the subsetOfGroup pattern, we may implement the nextClique pattern like follows:
 1 /* * the nextCliques pattern */
 2 pattern nextCliques ( g : Group , p : Person ) {
 3    neg find a l p h a b e t i c a l l y L a t e r M e m b e r O f G r o u p (g , p ) ;
 4    n == count find c o m m o n M o v i e O f G r o u p A n d P e r s o n (g , p , m ) ;
 5    check ( n >= 3) ;
 6    neg find union (g , p ) ;
 7 }
 8
 9 /* * p is a member of g for which another a lphabe ticall y previous member exists */
10 pattern a l p h a b e t i c a l l y L a t e r M e m b e r O f G r o u p ( g : Group , p : Person ) {
11    find memberOfGroup (m , g ) ;
12    Person . name (p , pName ) ;
13    Person . name (m , mName ) ;
14    check ( mName >= pName ) ;
15 }
16
17 /* * m is a common movie of g and p */
18 pattern c o m m o n M o v i e O f G r o u p A n d P e r s o n (g , p , m ) {
19    find c o m m o n M o v i e s O f G r o u p (g , m ) ;
20    Person . movies (p , m ) ;
21 }
22
23 /* * m is a common movie of g */
24 pattern c o m m o n M o v i e s O f G r o u p (g , m ) {
25    Group . commonMovies (g , m ) ;
26 }
27
28 /* * p is in g0 */
29 pattern union ( g0 , p ) {
30    find memberOfGroup (p , gx ) ;
31    find subsetOfGroup ( g0 , gx ) ;
32 }



     A.2.4     Comparator Code for Top-15

 1   class G r o u p S i z e C o m p a r a t o r implements Comparator < Group >{
 2
 3       override compare ( Group arg0 , Group arg1 ) {
 4         if ( arg0 . commonMovies . size < arg1 . commonMovies . size ) { return 1}
 5         else if ( arg0 . commonMovies . size == arg1 . commonMovies . size ) { return 0}
 6         else return -1;
 7       }
 8   }
 9
10   class G r o u p A V G C o m p a r a t o r implements Comparator < Group >{
11
12       override compare ( Group arg0 , Group arg1 ) {
13         if ( arg0 . avgRating < arg1 . avgRating ) { return 1;}
14         else if ( arg0 . avgRating == arg1 . avgRating ) { return 0;}
15         else return -1;
16       }
17   }