For the Boss class, I need the name of all the agents who made the sales with the highest value (something like: foreach agent, select agent name if he has
max(foreach command, total = total + price of the product * quantity from command).
How do I do this in OCL?

- 1,579
- 11
- 23
1 Answers
If you consider that you are at the root of the model (no context in particular) in order to select the 20
first top Agents:
Agent.allInstances()->sortedBy(- sale->collect(quantity*product.price)->sum())->subSequence(1, 20)
and from a Boss
instance:
self.workers->sortedBy(- sale->collect(quantity*product.price)->sum())->subSequence(1, 20)
The idea behind the request is (for the 1st one):
- get all the agents (
Agent.allInstances()
) - sort them (
...->sortedBy(...)
) - using the sum of their sales (
... sale->...->sum()
) - a "sale" is defined by the mult. of the quantity by the price of the referenced product (
quantity*product.price
) - for each sale, compute this stuff (
...sale->collect(...)
) - from this final result (the
sum
one), inverse the result to have the top in first positions (... - sale->collect()->sum()...
) - from this final list, select a sub sequence (
...->subSequence(1,X)
)
EDIT>
Just a detail about association class navigation (from the "OCL Specification", p.21)
To specify navigation to association classes (Job and Marriage in the example), OCL uses a dot and the name of the association class
Following the early version of the specification, the Association Class
name is said to be put as lower case, in the later version, the name is let untouched.
EDIT2>
In order to get the higher score and the agents name who hits this highest score:
let score : Integer = -(self.workers->collect(sale->collect(quantity*product.price)->sum())->sortedBy(i | -i)->first())
in self.workers->select(sale->collect(quantity*product.price)->sum() = score).name
The first let select the higher score (collect all the scores, sort them in reverse order and select the first element), then select all the workers who have a score equals to the previously computed one.

- 1,441
- 10
- 21
-
thanks! I saw your comment on my other post. I would like to know what does the '-' stand for before the "sale"? – Simply Me Dec 08 '15 at 15:09
-
What if there are more agents who have the same value? Example, agent1 and agent2 made sales totaling an amount of 100 each, and I do not know how much each did, but I need the names of all the agents who hit the max target. – Simply Me Dec 08 '15 at 15:10
-
1Just the unary minus operation in order to sort the list by reverse order in order to have the top agent in first position in the list. There is no `reverse` function in OCL (in Acceleo, there is, but not in "pure" OCL), so this is my trick to reverse the list. – Vincent Aranega Dec 08 '15 at 15:12
-
For the second point, you have to get all the agent that hit (or have more) than the amount, then just collect their names: `Agent.allInstances()->select(sale->collect(quantity*product.price)->sum() >= 100)->collect(name)` or `Agent.allInstances()->select(sale->collect(quantity*product.price)->sum() >= 100).name` for short (using the implicit collect operator). However, using the subSequence function, if there is 22 Agent with a score of 100, it only will get the 20 first names it will found. – Vincent Aranega Dec 08 '15 at 15:15
-
The only thing I don't get is how you select the maximul value from there? I mean... I have those values: 10, 100, 20, 100. I want to select the agents who have the sum() = maxValue This is my main problem... I need something like this: `Boss.workers->select(sale->collect(quantity*product.price)->sum() == maximumSum).name – Simply Me Dec 08 '15 at 15:41
-
Oh, ok, this is a little bit different, you want the higher score and the agents name who hits this highest score. You can do this in one line, but it will be very verbose, using a `let ... in ...` will ease the request writing. I edit my answer to write this down. – Vincent Aranega Dec 08 '15 at 15:48
-
so actually, the `score` stores the max value and then using the "in" clause, you check if the worker meets the max value or not? Great, thanks! This really helped. You are very good at OCL. – Simply Me Dec 08 '15 at 20:42
-
Yep, that's it, the `let ... in ...` expression let you define tmp variables (as in functionnal languages). The code associated to `score` finds the greatest value from the list of results (each result is the computed sale result of a each Agent), then, this score is used in the `in` expression in order to filter the Agent. From the filtered list, you can then retrieve the names. – Vincent Aranega Dec 08 '15 at 20:53