jruizgit / rules

Durable Rules Engine
MIT License
1.14k stars 207 forks source link

Rules based only on event properties #86

Open gandola opened 7 years ago

gandola commented 7 years ago

Hi,

I'm evaluating the library and I'm trying to do something like:

from durable.lang import *

with ruleset('m2'):
    @when_all(m.val < m.very_low)
    def low(c):
        print('Very Low')

    @when_start
    def start(host):
        host.post('m2', { 'val': 1, 'very_low':6})

run_all()

But I get the following error:

  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/durable/lang.py", line 635, in run_all
    main_host = create_host(databases, state_cache_size)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/durable/lang.py", line 627, in create_host
    main_host = engine.Host(ruleset_definitions, databases, state_cache_size)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/durable/engine.py", line 944, in __init__
    self.register_rulesets(None, ruleset_definitions)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/durable/engine.py", line 1019, in register_rulesets
    rulesets = Ruleset.create_rulesets(parent_name, self, ruleset_definitions, self._state_cache_size)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/durable/engine.py", line 542, in create_rulesets
    branches[name] = Ruleset(name, host, definition, state_cache_size)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/durable/engine.py", line 413, in __init__
    self._handle = rules.create_ruleset(state_cache_size, name, json.dumps(ruleset_definition, ensure_ascii=False))
rules.error: Could not create ruleset, error code: 102

But if I change the condition to something like: m.val < 10 it works well. So, Is it possible to have conditions based only on event properties (e.g: @when_all(m.val > m.avg + 2 * m.std)) ?

For now, my events are evaluated independently and I need simple rules like the above. However, I'm struggling to have this simple scenario working. I'm quite new using rule engines and this library specifically so I expect that doing something completely wrong or maybe I've misunderstood the concept :).

Thank you for your help, Cheers

jruizgit commented 7 years ago

Hi, thanks for posting the question. Unfortunately comparing events in rvalues is not supported. The engine has all the underpinnings to do that efficiently, I will add this to the list of features to implement.

While the code below will work, it is complex and not very efficient, as it pushes the evaluation to the redis server.

with ruleset('m2'):
    @when_all(c.first << +m.very_low,
              m.val < c.first.very_low)
    def low(c):
        print('Very Low')

    @when_start
    def start(host):
        host.post('m2', { 'val': 1, 'very_low':6})
macroach commented 7 years ago

Cool! I'm very interested in this as well.

ammarwm commented 7 years ago

Hi,

I faced the same problem and followed similar solution. In my case, the RHS of the condition is also set by the fact/event.

jruizgit commented 7 years ago

Hi, I have added support for using 'm' in rvalues. I also added validation for other invalid cases, which have been confusing. I will be updating the docs and finishing the ruby version this week. Please try pipy 0.33.108 and npm 0.36.70. Some examples:

from durable.lang import *

with ruleset('risk'):

    @when_all(c.first << m.amount > 100,
              c.second << m.amount > c.first.amount + m.amount / 2)
    def fraud_1(c):
        print('fraud detected ->{0}'.format(c.first.amount))
        print('fraud detected ->{0}'.format(c.second.amount))

    @when_all(m.debit > m.credit * 2)
    def fraud_2(c):
        print('debit {0} more than twice the credit {1}'.format(c.m.debit, c.m.credit))

    @when_all(m.debit > m.credit)
    def fraud_3(c):
        print('debit {0} greater than credit {1}'.format(c.m.debit, c.m.credit))

    @when_all(m.debit <= m.credit)
    def fraud_4(c):
        print('debit {0} less than or equal to credit {1}'.format(c.m.debit, c.m.credit))

    @when_start
    def start(host):    
        host.post('risk', { 'debit': 220, 'credit': 100 })
        host.post('risk', { 'debit': 150, 'credit': 100 })
        host.post('risk', { 'debit': 50, 'credit': 100 })
        host.post('risk', { 'amount': 200 })
        host.post('risk', { 'amount': 500 })

run_all()
macroach commented 7 years ago

Thanks for working on this @jruizgit ! I tried installing durable-rules 0.33.108 with pip, but it was unable to build. This was on a fresh virtualenv using Python 3.6. I've attached the log messages from my install attempt.

durable_install_fail_logs.txt

jruizgit commented 7 years ago

Hi, I introduced a bug when working on fact identity. The Microsoft C compiler does not support initializing arrays using the syntax: unsigned char counts[43] = {};. I have fixed it and published a new build. Please try version '0.33.109'. I have tested the node and python (3.6) versions in windows.