Оценка решений с использованием API движка DMN

Этот раздел перенесён из документации Camunda 7 и в дальнейшем будет доработан с учётом особенностей OpenBPM Engine

Интерфейс движка DMN предоставляет открытый доступ к методам для парсинга и оценки DMN-решений.

Парсинг решений

Решения могут быть получены через парсинг InputStream или преобразованием DmnModelInstance.

Пример ниже показывает, как происходит парсинг входящего потока (input stream):

// create a default DMN engine
DmnEngine dmnEngine = DmnEngineConfiguration
  .createDefaultDmnEngineConfiguration()
  .buildEngine();

InputStream inputStream = ...

// parse all decision from the input stream
List<DmnDecision> decisions = dmnEngine.parseDecisions(inputStream);

Следующий пример использует DMN Model API, чтобы сначала создать DmnModelInstance и затем произвести преобразование решений:

// create a default DMN engine
DmnEngine dmnEngine = DmnEngineConfiguration
  .createDefaultDmnEngineConfiguration()
  .buildEngine();

// read a DMN model instance from a file
DmnModelInstance dmnModelInstance = Dmn.readModelFromFile(...);

// parse the decisions
List<DmnDecision> decisions = dmnEngine.parseDecisions(dmnModelInstance);

Ключ решения

XML файл DMN может содержать многочисленные решения, сгруппированные по xref:openbpm:engine:reference/dmn/drg/index.adoc[графу требований к решению]. Чтобы отличать решения одно от другого, каждое решение должно иметь атрибут `id`.
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
  <decision id="first-decision" name="First Decision">
    <decisionTable>
      <output id="output1"/>
    </decisionTable>
  </decision>
  <decision id="second-decision" name="Second Decision">
    <decisionTable>
      <output id="output2"/>
    </decisionTable>
  </decision>
</definitions>

id решения в XML называется ключом (key) в контексте движка DMN. Чтобы выполнить парсинг только одного конкретного решения из DMN файла, вы задаете ключ решения, который соответствует атрибуту id в XML файле.

// create a default DMN engine
DmnEngine dmnEngine = DmnEngineConfiguration
  .createDefaultDmnEngineConfiguration()
  .buildEngine();

// read the DMN XML file as input stream
InputStream inputStream = ...

// parse only the decision with the key "second-decision"
DmnDecision decision = dmnEngine.parseDecision("second-decision", inputStream);

Парсинг графа требований к решениям

В дополнение к парсингу всех находящихся в графе требований к решениям (decision requirements graph, DRG) решений, движок DMN также может распарсить и сам DRG, извлекая его из InputStream или DmnModelInstance.

// parse the drg from an input stream
DmnDecisionRequirementsGraph drg = dmnEngine.parseDecisionRequirementsGraph(inputStream);

// get the keys of all containing decisions
Set<String> decisionKeys = drg.getDecisionKeys();

// get a containing decision by key
DmnDecision decision = drg.getDecision("decision");

// get all containing decisions
Collection<DmnDecision> decisions = drg.getDecisions();

DRG представлен в XML элементом definitions. Атрибут id для DRG в XML называется key в контексте движка DMN.

Только таблицы решений

Можно проверить, реализовано ли прошедшее парсинг решение в таблице решений, используя метод isDecisionTable().

// create a default DMN engine
DmnEngine dmnEngine = DmnEngineConfiguration
  .createDefaultDmnEngineConfiguration()
  .buildEngine();

// read the DMN XML file as input stream
InputStream inputStream = ...

// parse all decision from the input stream
List<DmnDecision> decisions = dmnEngine.parseDecisions(inputStream);

// get the first decision
DmnDecision decision = decisions.get(0);

// do something if it is a decision table
if (decision.isDecisionTable()) {
  // ...
}

Оценка решений

Чтобы оценить (или "исполнить") решение, либо передайте уже трансформированное DmnDecision, или используйте экземпляр DMN модели или input stream в комбинации с ключом решения.

Набор входных переменных должен быть предоставлен в качестве входной информации для процесса оценки.

// create a default DMN engine
DmnEngine dmnEngine = DmnEngineConfiguration
  .createDefaultDmnEngineConfiguration()
  .buildEngine();

// read the DMN XML file as input stream
InputStream inputStream = ...;

// parse the DMN decision from the input stream
DmnDecision decision = dmnEngine.parseDecision("decisionKey", inputStream);

// create the input variables
VariableMap variables = ...;

// evaluate the decision
result = dmnEngine.evaluateDecision(decision, variables);

// or if the decision is implemented as decision table then you can also use
result = dmnEngine.evaluateDecisionTable(decision, variables);

Передача переменных

Чтобы предоставить входные переменные для оценки решений, вы можете использовать Java Map<String, Object>, соответствующий либо VariableMap, либо VariableContext.

Следующий пример показывает, как использовать VariableMap.

// create the input variables
VariableMap variables = Variables.createVariables()
  .putValue("x", "camunda")
  .putValue("y", 2015);

// evaluate the decision with the input variables
result = dmnEngine.evaluateDecision(decision, variables);

В качестве альтернативы можно использовать VariableContext. Используйте VariableContext, если вам нужна поддержка lazy-загрузки переменных.

Интерпретация результатов решения

Процесс оценки DMN решения возвращает DmnDecisionResult. Если решение реализовано как таблица решения, тогда результатом является список результатов, удовлетворяющих правилам решения. Эти результаты представляют маппинг с имени элемента выходных данных на его значение.

Если же решение реализовано как литеральное выражение решения, тогда результатом является список, в котором содержится только одно вхождение. Это вхождение представляет значение выражения и имеет маппинг на имя переменной.

Приведем следующий пример принятия решения по выбору блюда.

Выбор блюда

Таблица решений на выходе возвращает desiredDish.

Предположим, что производится оценка решения по таблице со следующими входными переменными:

  • season: "Spring"

  • guestCount: 14

В таблице имеется подходящее правило для заданных входных значений. Таким образом, DmnDecisionResult состоит из одного DmnDecisionResultEntries, которое содержит ключ desiredDish.

Чтобы добраться до выходного значения, используется метод get() класса DmnDecisionResultEntries:

DmnDecisionResult decisionResult = dmnEngine.evaluateDecision(decision, variables);

// the size will be 1
int size = decisionResult.size();

// get the matching rule
DmnDecisionResultEntries ruleResult = decisionResult.get(0);

// get output values by name
Object result = ruleResult.get("desiredDish");

Результирующие объекты предоставляют дополнительные методы для удобства:

DmnDecisionResult decisionResult = dmnEngine.evaluateDecision(decision, variables);

// returns the first rule result
DmnDecisionResultEntries ruleResult = decisionResult.getFirstResult();

// returns first rule result
// but asserts that only a single one exists
decisionResult.getSingleResult();

// collects only the entries for an output column
decisionResult.collectEntries("desiredDish");

// returns the first output entry
ruleResult.getFirstEntry();

// also returns the first output entry
// but asserts that only a single one exists
ruleResult.getSingleEntry();

// shortcut to returns the single output entry of the single rule result
// - combine getSingleResult() and getSingleEntry()
decisionResult.getSingleEntry();

Отметим, что решение также может быть оценено с использованием метода evaluateDecisionTable(), если оно реализовано как таблица решений. В этом случае оценка возвращает DmnDecisionTableResult, что семантически эквивалентно и предоставляет те же методы, что и DmnDecisionResult.

Решения, требующее результатов других решениями

Если у решения имеются одно или несколько решений, чьи результаты ему необходимы, тогда эти необходимые решения оцениваются в первую очередь. Результаты этих оценок передаются как входные данные для оценки решения.

Возьмем нижеприведенный пример с выбором напитков.

Решение по напиткам

Следующая диаграмма требований к решению показывает, что решение по Beverages требует результатов решения по Dish (из предыдущего примера).

Выбрать напитки

При оценке решения по Beverages движок DMN сначала проведет оценку решения по Dish.

Предположим, что оценка решения производится со следующими входными переменными:

  • season: "Spring"

  • guestCount: 14

  • guestsWithChildren: false

С приведенными выше входными данными таблица решений Dish содержит одно подходящее правило и генерирует выходное значение Stew, которое соответствует выходной переменной desiredDish.

Выходные данные решения по Dish используются как входные для решения по Beverages. Это означает, что входное выражение desiredDish решения Beverages возвращает выходное значение Stew из решения Dish. В целом, решение может получить доступ к результатам (другими словами, выходным значениям) необходимых для них решений по имени их выходных данных.

По результатам решение Beverages имеет два подходящих правила и генерирует выходные значения Guiness и Water.

DmnDecision decision = dmnEngine.parseDecision("beverages", inputStream);

DmnDecisionResult decisionResult = dmnEngine.evaluateDecision(decision, variables);

List<String> beverages = decisionResult.collectEntries("beverages");

Hit policy для необходимых решений

Hit policy для неоьходимых решений может повлиять на результат, передаваемый в качестве входнях данных решению, которому они необходимы. Если необходимое решение имеет hit policy If the required decision has a COLLECT с агрегатором, тогда результат решения (выходное значение) может быть только агрегированным значением.

В случае hit policy с несколькими подходящими правилами, (то есть, COLLECT без агрегатора или RULE ORDER), выходные значения получают маппинг на список выходных значений, даже если нашлось только одно подходящее правило.

Лицензия и атрибуция

Эта документация была создана на базе материала "Camunda 7 Docs" от Camunda, находится под лицензией Creative Commons Attribution-ShareAlike 3.0 Unported License .

Оригинал документации: https://docs.camunda.org