-
Notifications
You must be signed in to change notification settings - Fork 139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Request: ability to add custom operators which are not automatically traversed for evaluation #116
Comments
Just to be clear, I'm happy to do the work for this change, just wondering what the appetite is for new functionality like this? |
OMG, sorry for the late response. Yes I think this is a really cool idea and I'd be happy to review (and consult and advise) a PR that achieves it. |
Hey @jwadhams , |
I have been evaluating JSON Logic for a use case and I want the ability to insert data into the logic. You can do this with primitives, but not objects. An example of how you can do it with primitives is very simple: "if": [
true,
"a",
"b"
] Where To prove this out, I did the following test: jsonLogic.add_operation('constant', (value) => {
return value;
}); Then: console.log(jsonLogic.apply({"constant": "hello"})); prints console.log(jsonLogic.apply({"constant": {"key": "hello"})); throws an error: That took me to this ticket and the related PR. I can confirm that if I use the JSON Logic version from the PR I get the results I desire, which is: jsonLogic.add_operation('constant', (value) => {
return value;
}, {
controlledExecution: true
});
console.log(jsonLogic.apply({"constant": "hello"}));
console.log(jsonLogic.apply({"constant": {"key": "hello"})); prints: [ "hello" ]
[ { "key": "hello" } ] One very interesting point is that Lines 213 to 216 in 0653b36
Lines 226 to 229 in 0653b36
Overall, I am very interested in this
|
I've been exploring an idea that I thought I'd share. Let's say this feature is implemented that let's you disable the depth-first traversal. You could write your own operation that does its own traversal! Which, would let you do something like this: console.log(jsonLogic.apply({
"traverse": {
"key1": "value1",
"key2": { "var": "dataKey1" },
"key3": {
"if": [
true,
{
"var": "dataKey1"
},
"foo"
]
},
"key4": {
"nestedKey": {
"var": "dataKey1"
}
}
}
}, { "dataKey1": "dataValue1" })); And get back: [
{
key1: 'value1',
key2: 'dataValue1',
key3: 'dataValue1',
key4: { nestedKey: 'dataValue1' }
}
] That's pretty cool! It would be a way of having a "template" JSON structure, then traversing it to replace certain parts of it using JSON Logic. The traversal I implemented to achieve the above did require adding an Expand to view "is_operation" feature and custom traversal opjsonLogic.is_operation = function (op) {
if (['if', '?:', 'and', 'or', 'filter', 'map', 'reduce', 'all', 'none', 'some'].indexOf(op) !== -1) {
return true;
}
if (operations.hasOwnProperty(op) && typeof operations[op] === 'function') {
return true;
} else if (op.indexOf('.') > 0) {
const sub_ops = String(op).split('.');
let operation = operations;
for (let i = 0; i < sub_ops.length; i++) {
if (!operation.hasOwnProperty(sub_ops[i])) {
return false;
}
// Descending into operations
operation = operation[sub_ops[i]];
}
return true;
}
return false;
};
function traverseJsonLogic(value, data, jsonlogic) {
if (Array.isArray(value)) {
const ret = [];
for (const i in value) {
if (typeof value[i] === 'object') {
ret[i] = traverseJsonLogic(value[i], data, jsonlogic);
} else {
ret[i] = value;
}
}
return ret;
}
if (typeof value === 'object' && Object.keys(value).length > 1) {
const ret = {};
for (const i in value) {
if (jsonLogic.is_operation(i)) {
throw new Error(`Operation key found on object: ${i}! An object that has multiple keys cannot have a key that is an operation.`);
}
ret[i] = traverseJsonLogic({[i]: value[i]}, data, jsonlogic)[i];
}
return ret;
}
if (typeof value === 'object' && !jsonLogic.is_operation(Object.keys(value)[0])) {
const op = Object.keys(value)[0];
return {
[op]: traverseJsonLogic(value[op], data, jsonlogic),
};
}
return jsonlogic.apply(value, data);
}
jsonLogic.add_operation('traverse', (value, data, jsonlogic) => {
return traverseJsonLogic(value, data, jsonlogic);;
}, {
controlledExecution: true
}); All that to say, this feature would open up significant flexibility / extensibility for JSON Logic. |
I did a very brief look to see what "is traversable" and found:
This isn't earth shattering, but I do like the idea of simply |
I might be having too much fun with this |
Hey @jwadhams, Any updates on when you might be able to review the linked PR? |
Some operators, for example
if
,map
,reduce
etc. have modified scope for evaluation, so that the parameters passed into these operators aren't automatically evaluated but instead conditionally evaluated based on the operators control structure.However, it appears the ability to add such behaviour with a custom operator is not currently supported
json-logic-js/logic.js
Line 231 in 9c805e9
There is already this large if-else chain within the
apply
method which could be easily modified to allow for custom operators to added and executed here (perhaps via some configuration when registering the custom operator), which would allow users to define whether their custom operator should automatically traverse or not.js-logic-engine another library for json-logic also has an implementation of a similar configuration when registering a custom operator with their
traverse
option.Would it be possible, to add a similar sort of functionality to this library when registering custom operators?
The text was updated successfully, but these errors were encountered: