TotalTechGeek / json-logic-engine

Construct complex rules with JSON & process them.
MIT License
42 stars 9 forks source link

Combine and/or to create more complex models #15

Open FlorianRuen opened 11 months ago

FlorianRuen commented 11 months ago

Hey,

I am currently working on a project, in which I am trying to build rules models (a bit like rules for a firewall) and I did not find, in the docs, an answer to some of my questions :

Hope you can help me to choose the right library !

TotalTechGeek commented 11 months ago

Hey @FlorianRuen,

it possible to combine or / and ? For example to make a rule which would be: the variable (a > 0 and b < 4) OR (b == 5 and a < 10) with multiples rules (here just two, but we can imagine more OR here)

Absolutely! It might be helpful to think of JSON-Logic as a dialect of Lisp. That specific rule would be:

{
        or: [
          {
            and: [
              {
                '>': [
                  {
                    var: 'a'
                  },
                  0
                ]
              },
              {
                '<': [
                  {
                    var: 'b'
                  },
                  4
                ]
              }
            ]
          },
          {
            and: [
              {
                '==': [
                  {
                    var: 'b'
                  },
                  5
                ]
              },
              {
                '<': [
                  {
                    var: 'a'
                  },
                  10
                ]
              }
            ]
          }
        ]
}

Additionally, and and or take in a variable number of arguments. So you wouldn't need to do

(and (> a 5) (and (< a 10) (== a 7))), you could do (and (> a 5) (< a 10) (== a 7))

Do you use any standard that can be parsed in another language ? For example, generate the json using JS/react, but apply rules with data/schema in Golang ?

The "standard" we use is JSON-Logic, and there's an ecosystem of other libraries that use it. https://jsonlogic.com/

This project is an alternative implementation of the mainline javascript project, to add support for asynchronous methods and performance optimizations via compilation.

Do you known if there is a compact and good UI generator to output this kind of schema (if the schema match some standards) or do I need to recreate something my myself ?

There are some projects listed on npm that provide native support for JSON Logic, but any rules builder UI should work (this should broaden the options available to your project). It's usually pretty simple to map from one rules dialect to another. If you need assistance with this, I'd be happy to help.

FlorianRuen commented 11 months ago

Great @TotalTechGeek, seems a very good implementation!

Just two more questions :

For the builder, I didn't really found a good library that can handle a form (with + to add rule) But, maybe I can create the form builder myself (maybe in opensource related to this json logic), using something like https://github.com/krismuniz/js-to-json-logic, to convert string to json logic

I will do some tests using sample data on many json logic, but seems a good way to create what I need!

TotalTechGeek commented 11 months ago

There is any limitation on types that works ? For example if I want to use BigInt (a > BigInt value)

Ah! Good callout!

BigInt will work with all of the comparison operators, but will not work with the arithmetic expressions.

For your use-case, it sounds like you're mainly interested in the comparisons & boolean logic, so it shouldn't impact you, but I'm going to try to resolve that in the next day or so.

While most types (and things like Maps & Sets) pass into the logic just fine, I actually did forget to consider BigInt in my test cases.

In addition to the result true / false, is it possible to get the reason (for example false because a not equals to 0 as error message or something), because I need the reason why the data not matched (user friendly) or do I need to parse myself

You could, but not by default out of the box. Importing an extension would be necessary.

As JSON Logic is more like a sandboxed Lisp dialect, it's better to think of this library as a performant expression evaluator.

If this functionality is necessary, while I could recommend "Hey, try importing these methods into the engine", the same extensions would need to be ported over to the other libraries (like in Golang), so that likely wouldn't be ideal.

So instead of changing the expressions, one option that is portable is to generate expressions that will return an error:

const detectErrors = engine.build({
  if: [
    { '<': [{ var: 'a' }, 5] },
    'a is less than 10',
    { '>': [{ var: 'a' }, 10] },
    'a is greater than 10',
    null
  ]
}) 

if (detectErrors({ a: 3 })) {
  // ... 
}
FlorianRuen commented 11 months ago

@TotalTechGeek thank you for your great advices !

If you need some example for BigInt, feel free to ask. At the moment, I don't think I will create some arithmeric operations using BigInt (but I will try to think if this use case can be helpful)

Regarding error detection, for the moment I will put it aside, because it somewhat complicates the operation (especially with a UI generator). But it's good to know that it is possible !

For the UI, I will use https://github.com/ukrbublik/react-awesome-query-builder But it seems, artihmeric operators like plus, minus, multiply and divide isn't supported by default (but maybe I can achieve using custom operators)

Do you have already used this UI library ? Any advice regarding arithmeric operators ?

Mihailoff commented 9 months ago

In addition to the result true / false, is it possible to get the reason (for example false because a not equals to 0 as error message or something), because I need the reason why the data not matched (user friendly) or do I need to parse myself

How about something like this?

{ and: [
  { log: 'a should be lees than b' },
  { '<': [{ var: 'a' }, { var: 'b' }] },
  { log: 'b should be lees than c' },
  { '<': [{ var: 'b' }, { var: 'c' }] },
  { log: null }
] }

The latest log message will explain where it stopped. I haven't tested this, just an idea. Writing a message can be impractical, maybe the engine could return/store the index/location of the latest evaluated rule before exiting!?

carloscco commented 3 weeks ago

Hi! I recently integrated this library into a project (great work btw 🙌), and I stumbled into this same need:

In addition to the result true / false, is it possible to get the reason (for example false because a not equals to 0 as error message or something), because I need the reason why the data not matched (user friendly) or do I need to parse myself

My first thought was similar to what @Mihailoff suggested, but I think it obscures the actual logic.

A second idea was having an "extension" of jsonlogic grammar, allowing an additional key message. I say "extension" because the second stated virtue of jsonlogic is:

  1. Consistent. {"operator" : ["values" ... ]} Always.

Conceptual example

"extended" logic definition:

{
    "or": [
      { "<": [{ "var": "a" }, { "var": "b" }], "message": "A should be less than B" },
      { "<": [{ "var": "b" }, { "var": "c" }], "message": "B should be less than C" }
    ]
}

"jsonlogic-compliant" logic definition:

{
    "or": [
      { "<": [{ "var": "a" }, { "var": "b" }] },
      { "<": [{ "var": "b" }, { "var": "c" }] }]
}

After executing with data { "a": 3, "b": 5, c: "4" }, somewhere engine stores/returns "or.1", which can be used to retrieve the message from the original object