jwadhams / json-logic-js

Build complex rules, serialize them as JSON, and execute them in JavaScript
MIT License
1.27k stars 139 forks source link

in operator doesn't support key in object or object in array of objects #27

Open deinspanjer opened 7 years ago

deinspanjer commented 7 years ago

Currently, "in" supports these cases: Primitive element in array: {"in":[ "Ringo", ["John", "Paul", "George", "Ringo"] ]} Substring in string: {"in":["Spring", "Springfield"]}

However, if the array you are testing for containment holds objects, in doesn't work: {"in": [ {"a": 1}, [ { "a":1 }, { "b":2 } ] ] } This gives the error Unrecognized operation a

It seems like it could also support testing for the existence of a key in an object: {"in": [ "a", { "a":1, "b":2 } ] } Which also gives the same Unrecognized operation a error.

jwadhams commented 7 years ago

Yeah, the problem is that all JsonLogic operators are executed depth-first, except if, and, and or.

I did this initially because (a) the original spec was all arithmetic and logic operators with no side effects, so no one cared if it did some work that got discarded and (b) it simplifies the internal code for every operator to assume they're working on primitives.

(a) stopped being true when I introduced add_operation When I couldn't count on operators being side-effect-free, control structures had to be more careful to not execute both consequent paths of an if statement regardless of the conditional.

We could pretty easily patch in to work for objects it gets as data -- this fails because the implementation of in doesn't expect object attributes, not because the parser thinks a is an operator:

jsonLogic = require('./logic.js');
data = {"stuff":{"a":1, "b":2}};
rule = {"in":["b", {"var":"stuff"}]};
jsonLogic.apply(rule, data);

Another improvement might be to increase the rigor of is_logic() to reject more things that are objects but couldn't be logic (e.g., has more than one key) so line 196 halts recursion early, and replace the exception at line 279 to returns the first parameter unmodified like 196 (one key but not a recognized operation).

The nuclear option would be to make every command responsible for parsing its own parameters, which would have the advantage of letting you introduce your own control structures and re-uniting the structures at 35-156 and 213-250. I haven't thought deeply about this yet, but I'll bet it has unpleasant externalities.

josephdpurcell commented 1 year ago

The functionality described in https://github.com/jwadhams/json-logic-js/issues/116 would enable someone to write their own operation to achieve this, I believe.