Project

General

Profile

Cruximplement » History » Version 68

Kaushal Alate, 18/10/2023 03:42 PM

1 4 Shuvam Misra
*(For a conceptual overview and design of Crux, see [[cruxdesign|this page]] if you haven't already.)*
2
3 35 Shuvam Misra
# Algorithms and data structures for the Crux BRE
4 1 Shuvam Misra
5
{{>toc}}
6 35 Shuvam Misra
7
This page documents the rules engine. It does not cover the flow engine.
8 1 Shuvam Misra
9
A rules engine implementation must include the following:
10
* **RULE SCHEMA**. A notation to specify the list of valid terms in a rule. This list will be separate for each class of entities. For instance, for items in inventory, the list of attributes may be:
11
  * Price
12
  * Full name
13
  * Age in stock
14
  * Quantity in inventory
15
16
    For vendors, the list could include:
17
  * Amount outstanding
18
  * Total value of business done  in the last financial year
19
20
* **RULE NOTATION**. A notation to specify the pattern and actions of a rule.
21
* **THE MATCHING ENGINE**. Something which will take an entity with all its attributes, apply each rule to it, and follow the trail of rules to come up with a list of actions which will emerge.
22
23
So, if these three can be designed and then implemented, the core of a rules engine or a flow engine can be built.
24
25 2 Shuvam Misra
## Representing the schema of patterns
26 1 Shuvam Misra
27
If using JSON, the schema of all valid patterns may be represented in structures of this form:
28
29
``` json
30
"patternschema": {
31
    "class": "inventoryitems",
32
    "attr": [{
33
        "name": "cat",
34 47 Kaushal Alate
        "valtype": "enum",
35 1 Shuvam Misra
        "vals": [ "textbook", "notebook", "stationery", "refbooks" ]
36
    },{
37
        "name": "mrp",
38 47 Kaushal Alate
        "valtype": "float"
39 1 Shuvam Misra
    },{
40
        "name": "fullname",
41 47 Kaushal Alate
        "valtype": "str",
42 1 Shuvam Misra
    },{
43
        "name": "ageinstock",
44 47 Kaushal Alate
        "valtype": "int"
45 1 Shuvam Misra
    },{
46
        "name": "inventoryqty",
47 47 Kaushal Alate
        "valtype": "int"
48 1 Shuvam Misra
    }]
49
}
50
```
51
52 41 Kaushal Alate
In this example, the object `patternschema` is the schema for one class of entities. This schema says that for rules which work on entities of class `inventoryitems`, there are five attributes available which may be used to make patterns. Each attribute has a type. Boolean, enum types, integers, floating point numbers, timestamps (`ts`) and strings are supported. The example above does not have any attribute of type `ts` or  `bool`.
53 1 Shuvam Misra
54 65 Kaushal Alate
Initial examples have discussed inventory items and vendors. The `patternschema` block above is for inventory items. If the schema of patterns for vendors needed to be specified, there would be a second `patternschema` with `“class”: “vendors”`
55 1 Shuvam Misra
56 5 Shuvam Misra
While the fields in the example above are adequate from a purely functional point of view, it may be necessary to have some additional metadata to allow the building of a good UI which will allow users to manage these schema objects. So, an augmented data structure may look like this:
57
58
``` json
59
"patternschema": {
60
    "class": "inventoryitems",
61
    "attr": [{
62
        "name": "cat",
63
        "shortdesc": "Category of item",
64
        "longdesc": "Each item can belong to one of the following categories: textbooks, notebooks, stationery, or reference books.",
65 47 Kaushal Alate
        "valtype": "enum",
66 5 Shuvam Misra
        "vals": [ "textbook", "notebook", "stationery", "refbooks" ],
67
        "enumdesc": [ "Text books", "Notebooks", "Stationery and miscellaneous items", "Reference books, library books" ]
68
    },{
69
        "name": "mrp",
70
        "shortdesc": "Maximum retail price",
71
        "longdesc": "The maximum retail price of the item as declared by the manufacturer."
72 47 Kaushal Alate
        "valtype": "float",
73 5 Shuvam Misra
        "valmax": 20000,
74
        "valmin": 0
75
    },{
76
        "name": "fullname",
77
        "shortdesc": "Full name of item",
78
        "longdesc": "The full human-readable name of the item. Not unique, therefore sometimes confusing.",         
79 47 Kaushal Alate
        "valtype": "str",
80 5 Shuvam Misra
        "lenmax": 40,
81
        "lenmin": 5
82
    },{
83
        "name": "ageinstock",
84
        "shortdesc": "Age in stock, in days",
85 65 Kaushal Alate
        "longdesc": "The age in days that the oldest sample of this item has been lying in stock",
86 47 Kaushal Alate
        "valtype": "int",
87 5 Shuvam Misra
        "valmax": 1000,
88
        "valmin": 1
89
    },{
90
        "name": "inventoryqty",
91
        "shortdesc": "Number of items in inventory",
92 65 Kaushal Alate
        "longdesc": "How many of these items are currently present in the inventory",
93 47 Kaushal Alate
        "valtype": "int",
94 5 Shuvam Misra
        "valmax": 10000,
95
        "valmin": 0
96
    }]
97
}
98
```
99
100 66 Kaushal Alate
Here, the `shortdesc` and `longdesc` are useful "attributes" of each attribute, for displaying labels and help text in any UI which is displayed to the human user who manages the rules for entities of this class. The `valmax`, `valmin`, `lenmax`, `lenmin`, allow the system to enforce some sanity checks on the patterns defined in any rules for this entity.
101 5 Shuvam Misra
102 2 Shuvam Misra
## Representing the schema of actions
103 1 Shuvam Misra
104 42 Kaushal Alate
The schema of the action section of rules is simpler than patterns. Each rule's action section will contain a set of zero or more words, each denoting a task, and zero or more property assignments. There is no need for any type specification, etc.
105
* An example of a task word: `invitefordiwali`
106
* An example of a property assignment: `discount=7`
107 1 Shuvam Misra
108 42 Kaushal Alate
So, the schema of the actions will just specify the valid task names and the property names for assignments.
109 1 Shuvam Misra
110
``` json
111
"actionschema": {
112
    "class": "inventoryitems",
113 43 Kaushal Alate
    "tasks": [ "invitefordiwali", "allowretailsale", "assigntotrash" ],
114
    "properties": [ "discount", "shipby" ],
115 1 Shuvam Misra
}
116
```
117 43 Kaushal Alate
The schema of actions above indicates that there are three tasks, any or all of which may be present in any rule for this class of entities. There are two properties which may be assigned values by any rule.
118 1 Shuvam Misra
119
Putting the `patternschema` and `actionschema` blocks together, a better representation for the full schema for a class of entities will be:
120
121
``` json
122
"ruleschema": {
123
    "class": "inventoryitems",
124
    "patternschema": {
125
        "attr": [{
126
            "name": "cat",
127 47 Kaushal Alate
            "valtype": "enum",
128 1 Shuvam Misra
            "vals": [ "textbook", "notebook", "stationery", "refbooks" ]
129
        },{
130
            "name": "mrp",
131 47 Kaushal Alate
            "valtype": "float"
132 1 Shuvam Misra
        },{
133
            "name": "fullname",
134 47 Kaushal Alate
            "valtype": "str",
135 1 Shuvam Misra
        },{
136
            "name": "ageinstock",
137 47 Kaushal Alate
            "valtype": "int"
138 1 Shuvam Misra
        },{
139
            "name": "inventoryqty",
140 47 Kaushal Alate
            "valtype": "int"
141 1 Shuvam Misra
        }]
142
    }
143
    "actionschema": {
144 43 Kaushal Alate
        "tasks": [ "invitefordiwali", "allowretailsale", "assigntotrash" ],
145
        "properties": [ "discount", "shipby" ],
146 1 Shuvam Misra
    }
147
}
148
```
149
150
There will need to be one such `ruleschema` block for each class.
151
152 2 Shuvam Misra
## Representing a pattern
153 1 Shuvam Misra
154 6 Shuvam Misra
Below is an example of a pattern of a rule, which conforms to the schema example given above.
155
156 1 Shuvam Misra
``` json
157 44 Kaushal Alate
"rulepattern": [{
158 55 Kaushal Alate
        "attrname": "cat",
159 1 Shuvam Misra
        "op": "eq",
160 55 Kaushal Alate
        "attrval": "textbook"
161 1 Shuvam Misra
    },{
162 55 Kaushal Alate
        "attrname": "mrp",
163 1 Shuvam Misra
        "op": "ge",
164 55 Kaushal Alate
        "attrval": 2000
165 1 Shuvam Misra
    },{
166 55 Kaushal Alate
        "attrname": "ageinstock",
167 1 Shuvam Misra
        "op": "ge",
168 55 Kaushal Alate
        "attrval": 90
169 1 Shuvam Misra
    },{
170 55 Kaushal Alate
        "attrname": "invitefordiwali",
171 22 Shuvam Misra
        "op": "eq",
172 55 Kaushal Alate
        "attrval": true
173 44 Kaushal Alate
    }
174
}]
175 1 Shuvam Misra
```
176
177
If a rule has this pattern, it will match any entity which falls in the class `inventoryitems` which
178
* is of type textbook
179
* has MRP (max retail price) greater than INR 2000
180 25 Shuvam Misra
* has been in stock longer than 89 days 
181 44 Kaushal Alate
* one of the earlier rules matched against this entity has added the task `invitefordiwali` to the action set of this entity
182 22 Shuvam Misra
183 67 Kaushal Alate
It is important to note that a pattern does not need to have just the attributes listed in `patternschema`. It may also include tasks listed in the `actionschema`. Each such task becomes an implicit attribute of type `bool` for this class.
184 1 Shuvam Misra
185
For attributes which are of type `int`, `float`, `str` and `ts`, the following comparison operators are available:
186
* Greater than or equal to: `ge`
187
* Greater than: `gt`
188
* Less than or equal to: `le`
189
* Less than: `lt`
190
* Equal to: `eq`
191
* Not equal to: `ne`
192
193
Collation sequences for strings are system dependent, and will need to be standardised so that they work reliably across programming languages and Unicode strings in any language. That's an implementation issue.
194
195 21 Shuvam Misra
For `enum` and `bool` types, only `eq` and `ne` are available.
196 1 Shuvam Misra
197 2 Shuvam Misra
## Representing an action
198 1 Shuvam Misra
199
A rule has a set of one or more actions. The following are all examples of the action section of rules:
200
* `invitefordiwali`
201 46 Kaushal Alate
* `shipby=fedex`
202 1 Shuvam Misra
* `discount=7`
203
* `shipwithoutpo`
204
* `CALL=intlbiz`
205
206 48 Kaushal Alate
The words that identify actions, *e.g.* `invitefordiwali`, will automatically be converted to lower-case and stored in the system. Reserved instruction names like `THENCALL`, `ELSECALL`, `RETURN`, `EXIT`, will always be in uppercase. Property assignments can include multi-word strings, *e.g.*
207 1 Shuvam Misra
* `reprimand=This cannot go on any longer`
208
209 48 Kaushal Alate
The action portion of a rule can have zero or one occurrence of a `THENCALL`, `ELSECALL`, a `RETURN`, and an `EXIT`. If it contains both a `RETURN` and an `EXIT`, then the `RETURN` will be ignored.
210 1 Shuvam Misra
211
The action portion of a rule will have the following structure, shown here as an example:
212
``` json
213
"ruleactions": {
214
    "tasks": [ "christmassale", "vipsupport" ],
215 46 Kaushal Alate
    "properties": [ {"shipby", "fedex" } ],
216 48 Kaushal Alate
    "thencall": "internationalrules",
217
    "elsecall": "domesticrules",
218 1 Shuvam Misra
    "return": true,
219
    "exit": false
220
}
221
```
222 46 Kaushal Alate
This example shows all five fields of `ruleactions`, but in reality, some of the fields will typically be missing from most of the rules.
223 1 Shuvam Misra
224 2 Shuvam Misra
## An entire rule
225 1 Shuvam Misra
226
This is what an entire rule looks like:
227
228
``` json
229
"rule": {
230
    "class": "inventoryitems",
231
    "rulepattern": [{
232 55 Kaushal Alate
        "attrname": "cat",
233 1 Shuvam Misra
        "op": "eq",
234 55 Kaushal Alate
        "attrval": "textbook"
235 1 Shuvam Misra
    },{
236 55 Kaushal Alate
        "attrname": "mrp",
237 1 Shuvam Misra
        "op": "ge",
238 55 Kaushal Alate
        "attrval": 5000
239 1 Shuvam Misra
    }],
240
    "ruleactions": {
241 48 Kaushal Alate
        "tasks": [ "christmassale" ],
242
        "properties": [ {"shipby", "fedex" } ]
243 1 Shuvam Misra
    }
244
}
245
```
246
247 48 Kaushal Alate
This structure represents one rule. The rule applies to entities of class `inventoryitems`. It has a pattern section which tries to match two attributes and an action section which throws up one task and one property assignment.
248 1 Shuvam Misra
249 68 Kaushal Alate
A ruleset has a version number, which is incremented whenever the rule is updated. This number is for internal logging and rule engine debugging.
250 1 Shuvam Misra
251
An array of such structures is a set of rules, and will be traversed in the order in which the rules appear in the array. Named rulesets will be represented thus:
252
``` json
253
"ruleset": {
254 40 Shuvam Misra
    "ver": 5,
255 1 Shuvam Misra
    "class": "inventoryitems",
256
    "setname": "overseaspo",
257
    "rules": [{
258
        "rulepattern": {
259
            :
260
            :
261
        },
262
        "ruleactions": {
263
            :
264
            :
265
        }
266
    }, {
267
        "rulepattern": {
268
            :
269
            :
270
        },
271
        "ruleactions": {
272
            :
273
            :
274
        }
275
    }]
276
}
277
```
278 48 Kaushal Alate
The example above shows a ruleset named `overseaspo` for class `inventoryitems` which has two rules. This ruleset may be invoked from any other rule with the action `THENCALL=overseaspo` (or `ELSECALL=overseaspo`).
279 1 Shuvam Misra
280 2 Shuvam Misra
## The schema manager
281 1 Shuvam Misra
282 7 Shuvam Misra
The schema for each class of entities may be written by hand using a text editor. JSON or YAML files are easy to write. If the schema of one class has less than a dozen attributes, it may be short enough to edit or audit by hand. However, a tool to manage and maintain the schema eliminates typos and enforces various types of consistency, and a second-level implementation of a schema manager may also enforce authorisation policies.
283 1 Shuvam Misra
284
A schema manager will have the following features:
285
* It will allow the user to create new instances of `ruleschema`
286
* It will sharply restrict editing of, and prevent deletion of any `patternschema` block or `actionschema` block if there are rules defined in the rules engine for this class of entities. In other words, schema are editable only as long as there are no rules for the class. The only kind of editing it will permit for “live” schema are
287
  * the addition of additional attributes in a `patternschema` or
288 49 Kaushal Alate
  * additional tasks or properties in an `actionschema`.
289 1 Shuvam Misra
* It will ensure that there is no scope for typos when defining the schema.
290
291 3 Shuvam Misra
## The rule manager
292 1 Shuvam Misra
293
The rule manager will allow a user to manage rules. Core functionality:
294
* It will provide a user interface to let the user edit rules.
295
* It will check each rule against the schema for the class, and will not give the user the opportunity to define any rule inconsistent with the schema.
296
* It will allow the user to move a rule up or down in the sequence, since ordering is important.
297 49 Kaushal Alate
* If a rule is being defined with a `THENCALL/ELSECALL` action, then the rule manager will ensure that a ruleset with that target name exists.
298
* Most important: it will provide a testing facility by which sample entities may be submitted to the rule engine for testing, and the rule manager will display a full trace showing which rules were attempted to match, which rules actually matched, and how the result set of tasks and properties grew with each step. This feature will be provided without having to save the rule changes.
299 1 Shuvam Misra
* Finally, when the editing session is complete and all rulesets need to be saved, it will perform a detailed cross-validation of all rules across each other to ensure consistency. If there is any inconsistency, it will give readable explanations of the problems and not permit saving of the updates.
300
301 2 Shuvam Misra
## The matching engine
302 1 Shuvam Misra
303 49 Kaushal Alate
The matching engine has a one-line job. It will take a full set of attributes of one entity, apply all the rules which apply to its class, and return with the list of tasks and properties from all the matching rules.
304 1 Shuvam Misra
305 11 Shuvam Misra
The operation of the engine, in a highly simplified notation, is:
306
```
307
for each rule in the ruleset do
308
    match the pattern of the rule with the entity
309
    if the pattern matches, then
310 49 Kaushal Alate
        collect the tasks and properties from the rule into the actionset
311 11 Shuvam Misra
    endif
312
endfor
313
```
314 1 Shuvam Misra
315 2 Shuvam Misra
### Matching one rule's pattern
316 1 Shuvam Misra
317 50 Kaushal Alate
The algorithm for the matching of one rule's pattern is shown below. Here, it is assumed that the object being matched is in `entity` and pattern of the rule being matched is in `rulepattern`. It is assumed that the matching engine may have proceeded some distance in its matching process, and may have collected zero or more tasks or properties in its `actionset`.
318 1 Shuvam Misra
```
319 51 Kaushal Alate
func matchPattern()
320 18 Shuvam Misra
    input parameters: entity, rulepattern, actionset
321 1 Shuvam Misra
    returns patternmatch: boolean
322
323 16 Shuvam Misra
#
324
# In this loop we iterate through terms from our pattern array
325
# one by one
326 1 Shuvam Misra
#
327 51 Kaushal Alate
for term in rulepattern do
328 18 Shuvam Misra
    #
329
    # We get into a loop, stepping through the attributes of this
330
    # entity to pull out the value of the attribute
331 62 Kaushal Alate
    # we will now be matching against the patternterm we have selected
332
    # for this iteration of the outer loop
333 18 Shuvam Misra
    #
334 51 Kaushal Alate
    entityattrval = null
335
    for entityattr in entity.attrs do
336 62 Kaushal Alate
        if entityattr.name == term.attrname then
337 51 Kaushal Alate
            entityattrval = entityattr.val
338 1 Shuvam Misra
        endif
339
    endfor
340
341 51 Kaushal Alate
    if entityattrval == null then
342 19 Shuvam Misra
        #
343
        # We reach here if none of the attributes in the entity
344 51 Kaushal Alate
        # has a name matching the attribute in the term in the pattern array.
345
        # We still need to check if this pattern term matches a task
346
        # with which this entity has been tagged (a task that has already
347
        # been collected from matching a previous rule).
348 19 Shuvam Misra
        #
349 51 Kaushal Alate
        # So we will now cycle through the tasks in the actionset
350 62 Kaushal Alate
        # to see if any of those matches this term.
351 19 Shuvam Misra
        #
352 51 Kaushal Alate
        for task in actionset.tasks do
353 62 Kaushal Alate
            if task == term.attrname then
354 19 Shuvam Misra
                #
355 51 Kaushal Alate
                # Bingo! We have found a task which matches
356
                # the name of the attribute in the pattern term.
357 19 Shuvam Misra
                #
358 51 Kaushal Alate
                entityattrval = "true"
359 18 Shuvam Misra
            endif
360 19 Shuvam Misra
        endfor
361 18 Shuvam Misra
    endif
362
363 52 Kaushal Alate
    if entityattrval == null then
364 19 Shuvam Misra
        #
365 62 Kaushal Alate
        # If we reach here, it means that the task that is represented by term.attrname is
366 52 Kaushal Alate
        # not present in the actionset.
367
        # 
368
        entityattrval = "false"
369 19 Shuvam Misra
    endif
370
371 62 Kaushal Alate
    case term.op in
372 1 Shuvam Misra
    "eq":
373 62 Kaushal Alate
        if entityattrval != term.attrval then
374 1 Shuvam Misra
            return false
375
        endif
376
    "ne":
377 62 Kaushal Alate
        if entityattrval == term.attrval then
378 1 Shuvam Misra
            return false
379
        endif
380
    endcase
381 62 Kaushal Alate
    if term.type in [ "int", "float", "ts", "str" ] then
382
        case term.op in
383 1 Shuvam Misra
        "le":
384 62 Kaushal Alate
            if entityattrval > term.attrval then
385 1 Shuvam Misra
                return false
386
            endif
387
        "lt":
388 62 Kaushal Alate
            if entityattrval >= term.attrval then
389 1 Shuvam Misra
                return false
390
            endif
391
        "ge":
392 62 Kaushal Alate
            if entityattrval < term.attrval then
393 1 Shuvam Misra
                return false
394
            endif
395
        "gt":
396 62 Kaushal Alate
            if entityattrval <= term.attrval then
397 1 Shuvam Misra
                return false
398
            endif
399
        default:
400
            log error with priority = CRITICAL: "system inconsistency with BRE rule terms"
401
        endcase
402
    endif
403
endfor
404
405
return true
406 2 Shuvam Misra
```
407 1 Shuvam Misra
408 62 Kaushal Alate
Note how the algorithm cycles through the `actionset` of the entity, trying to see of any of the tasks in `actionset` matches the `term`. This aspect of the matching algorithm has been discussed in [[Cruxdesign#Tags|the explanation of the idea of tasks as tags]].
409 39 Shuvam Misra
410 1 Shuvam Misra
### Collecting the actions from one rule
411
412 53 Kaushal Alate
If the pattern for one rule matches the entity being processed, then the tasks of that rule will need to be added to the action set for that entity. Here we assume that the result of the action-collection function will return an object of the following structure. This object will be passed as input to the action-collecting function, and a (possibly extended) object will be returned, after merging the input object with the tasks and properties from the rule just matched. The object structure will be:
413 1 Shuvam Misra
``` json
414
"actionset": {
415 53 Kaushal Alate
    "tasks": [ "dodiscount", "yearendsale" ],
416
    "properties": [ {"shipby", "fedex"} ],
417 8 Shuvam Misra
}
418 1 Shuvam Misra
```
419 53 Kaushal Alate
These two fields will always be present in the object. The `tasks` field will carry an array of strings, which will be a union set of all the tasks collected from rules matched so far. The `properties` field will carry an array of properties, which are objects containing two strings: property name and property value.
420 1 Shuvam Misra
421 53 Kaushal Alate
Performing a set union of tasks is straightforward. Performing a set union of property assignments requires choosing one property value when a particular property already exists in the `actionset` and the current ruleaction also assigns a value to that property. In that case, the old value of the property will be overwritten by the new value.
422 1 Shuvam Misra
423
```
424
function collectActions()
425
input parameters: actionset, ruleactions
426
    returns actionset
427 14 Shuvam Misra
428 53 Kaushal Alate
actionset.tasks = actionset.tasks UNION ruleactions.tasks
429
actionset.properties = actionset.properties UNION ruleactions.properties
430 1 Shuvam Misra
431
return actionset
432
```
433 13 Shuvam Misra
434 1 Shuvam Misra
### Representing one entity
435 14 Shuvam Misra
436 54 Kaushal Alate
The matching engine matches all the rules of a ruleset against one instance of a class, like one instance of `vendor` or `inventoryitem`. How do we represent this object instance, when the type and the fields are all dynamically determined at runtime and vary from invocation to invocation? Here is one example:
437 1 Shuvam Misra
``` json
438
"inputentity": {
439
    "class": "inventoryitems",
440 63 Kaushal Alate
    "attrs": [{
441 14 Shuvam Misra
        "name": "cat",
442
        "val": "refbook"
443
    },{
444
        "name": "mrp",
445
        "val": "1350"
446
    },{
447
        "name": "fullname",
448
        "val": "Advanced Level Physics, 2/ed"
449
    },{
450
        "name": "ageinstock",
451
        "val": "20"
452
    },{
453
        "name": "inventoryqty",
454 1 Shuvam Misra
        "val": "540"
455
    }]
456 14 Shuvam Misra
}
457 1 Shuvam Misra
```
458 56 Kaushal Alate
As this example highlights, all the values supplied are of type string, so that they may be converted from strings to their respective types later. This allows the data structure for specifying an object instance to be strongly typed and still allow attributes of all types to be captured. One more point illustrated is that **all attributes in the `patternschema` of the class must be present** in each object instance of that class.
459 14 Shuvam Misra
460 1 Shuvam Misra
This is the way the entity will be submitted to the matching engine for processing.
461 13 Shuvam Misra
462 27 Shuvam Misra
### `doMatch()`: the matching function
463 13 Shuvam Misra
464 56 Kaushal Alate
This engine will go through rules one after another, and for each rule, it will call `matchPattern()`. If `matchPattern()` returns `true`, it will call `collectActions()` to update the `actionset`
465 14 Shuvam Misra
466 56 Kaushal Alate
This engine will be implemented by the `doMatch()` function, which will occasionally call itself recursively. It will be called with three parameters:
467 14 Shuvam Misra
* an `inputentity`, which will be matched against the ruleset
468 1 Shuvam Misra
* a `ruleset` which will be traversed by the engine
469 56 Kaushal Alate
* an `actionset`, which collects the results of the action matching
470 1 Shuvam Misra
471
The pseudocode has been written with the assumption that parameters are all pass-by-value.
472
473
So, the `doMatch()` engine will work in the following way:
474 14 Shuvam Misra
```
475 1 Shuvam Misra
function doMatch()
476 58 Kaushal Alate
input parameters: entity, ruleset, actionset
477
    returns actionset: ActionSet, willexit: boolean 
478 14 Shuvam Misra
479 1 Shuvam Misra
for each rule in ruleset do
480 58 Kaushal Alate
    if matchPattern(entity, rule.rulepattern, actionset) == true then
481 57 Kaushal Alate
        actionset = collectActions(actionset, rule.ruleactions)
482 58 Kaushal Alate
        willexit = false
483 1 Shuvam Misra
        #
484
        # Check if there's a THENCALL instruction
485
        #
486 57 Kaushal Alate
        if rule.ruleactions.thencall is not null then
487 1 Shuvam Misra
            settocall = rule.ruleactions.thencall
488 58 Kaushal Alate
            if settocall.class != entity.class then
489
                log error with priority = CRITICAL: 
490
                    "system inconsistency with BRE rule terms, attempting to call ", settocall, " from ", ruleset.setname, "!"
491 1 Shuvam Misra
            endif
492 58 Kaushal Alate
            actionset, willexit = doMatch(entity, settocall, actionset)
493 1 Shuvam Misra
        endif
494
        #
495 58 Kaushal Alate
        # If the called ruleset has set EXIT to true, or if the current ruleactions has an EXIT,
496
        # then we need to exit, and our caller too needs to exit, ad infinitum
497 14 Shuvam Misra
        #
498 58 Kaushal Alate
        if willexit OR rule.ruleactions.willexit then
499
            return actionset, true
500 15 Shuvam Misra
        endif
501
        #
502 58 Kaushal Alate
        # If there is no EXIT in ruleactions, check if there is a RETURN instruction
503 57 Kaushal Alate
        #
504 58 Kaushal Alate
        if rule.ruleactions.willreturn then
505
            return actionset, false
506 57 Kaushal Alate
        endif
507 59 Kaushal Alate
    else if rule.ruleactions.elsecall is not null
508 58 Kaushal Alate
        settocall = rule.ruleactions.elsecall
509 59 Kaushal Alate
        if settocall.class != entity.class then
510 58 Kaushal Alate
            log error with priority = CRITICAL: 
511
                "system inconsistency with BRE rule terms, attempting to call ", settocall, " from ", ruleset.setname, "!"
512 59 Kaushal Alate
        endif
513 64 Kaushal Alate
        actionset, willexit = doMatch(entity, settocall, actionset)
514 59 Kaushal Alate
        if willexit
515 64 Kaushal Alate
            return actionset, true
516 59 Kaushal Alate
        endif
517 58 Kaushal Alate
    endif
518 14 Shuvam Misra
    #
519
    # We come here because we've done one rule and we've neither been thrown
520
    # out by an EXIT nor a RETURN clause. So we now loop to the next rule in
521
    # our ruleset.
522 13 Shuvam Misra
    #
523 14 Shuvam Misra
endfor
524 10 Shuvam Misra
525 58 Kaushal Alate
return actionset, false
526 14 Shuvam Misra
```
527 1 Shuvam Misra
528 24 Shuvam Misra
This matching engine will be able to traverse all rulesets, make "subroutine calls" from one ruleset to another, and finally come up with a consolidated `actionset`.
529
530
The outermost calling code which calls the outermost layer of `doMatch()` for a given entity will initialise an empty `actionset` and pass it in. After all the ruleset traversals, `doMatch()` will return with a loaded `actionset`, which will then be returned to the client of the BRE.
531 1 Shuvam Misra
532 36 Shuvam Misra
## Operations supported by the BRE
533 1 Shuvam Misra
534 29 Shuvam Misra
The BRE must support the following set of operations:
535 32 Shuvam Misra
* Schema management:
536
  * `schemaAdd()`: add a new rule schema, for a new class
537
  * `schemaUpdate()`: update an existing schema
538
  * `schemaDelete()`: delete an existing schema
539
  * `schemaList()`: list all the schema for all classes
540
  * `schemaGet()`: get the full schema for one class, given the class name
541
* Ruleset management:
542
  * `rulesetAdd()`: add a new ruleset for a class, with one or more rules
543
  * `rulesetUpdate()`: update an existing ruleset
544
  * `rulesetDelete()`: delete a ruleset
545
  * `rulesetList()`: list all rulesets for a class
546
  * `rulesetGet()`: get the full ruleset, given the class name and the ruleset name
547
* Query the BRE and get the list of actions and attributes for an entity:
548 61 Kaushal Alate
  * `doMatch()`: submit an entity and pull out the final set of tasks and properties. This call will fail unless the `inputentity` carries all the attributes listed in the schema of its class.
549 32 Shuvam Misra
  * `getAttrSet()`: take a class name, pull out from the `patternschema` all the attributes listed against that class, with full details. This is useful to let the caller know what attributes are to be specified when calling `doMatch()`.
550 33 Shuvam Misra
* Rule engine management:
551
  * `setConfig()`: set one or more global configuration parameters of the engine
552
  * `getConfig()`: get values of one or more global config parameters of the engine
553
  * `setAuth()`: set authorization rules to allow access to the engine
554
  * `getAuth()`: get authorization rules which control access to the engine
555
  * `shutdown()`: shut down the engine
556 36 Shuvam Misra
  * `sysReset()`: the equivalent of a factory reset. All configuration reverts to that which is present in a fresh install, all currently active processing threads are aborted and all schema, rulesets and other information is lost.
557 37 Shuvam Misra
558
The actual API specs will be on [[cruxapi|a separate page]], but the list of operations have been listed here in the typical syntax of API calls just to indicate the operations which the BRE will support. Only when we look at a complete list of operations do we see the entire scope of this system.