Skip to content
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

Custom functions #100

Closed
meric opened this issue Oct 30, 2015 · 7 comments
Closed

Custom functions #100

meric opened this issue Oct 30, 2015 · 7 comments

Comments

@meric
Copy link

meric commented Oct 30, 2015

What are your thoughts about adding a method to register custom functions directly into RuntimeFunctions class in functions.py?

JMESPath is almost good enough to use as a domain specific language for general language transforming objects. You can sneak in literals in the multi-select hash. You can filter for values, and transform them to booleans using <=, == operators. There's some support for making sure values are numbers.

However I don't see anyway to do something like "if value is x, return y" where you show a literal if a condition matches. There's no way to convert conditions to an arbitrary literal - if a value in a multi-select hash is going to be a literal, it has to be of the same value no matter what.

I can see a possible workaround if custom functions on JMESPath. E.g. if I implement the "if" function for use, I can do something like:

search("if(bar==`1`, `hello`, `world`)", {'bar': '1'})

This would return the literal hello if the bar key is 1, otherwise it returns world. The only issue is the current python implementation means its going to be hacky to do this. You have to override multiple classes, in functions.py and override the TreeInterpreter and the ParsedResult classes as well.

I think if custom functions were desired, it would be much more elegant if there is a method to register them directly into the RuntimeFunctions in functions.py, rather than either forcing a fork or overriding a litany of classes.

What do you think?

@jamesls
Copy link
Member

jamesls commented Oct 30, 2015

I agree that I would like to have a way to register custom functions in a simple way. Some of the other language implementation of JMESPath support this so I'd like to do so with python. Let me see what I can some up with.

Additionally, I'm thinking that it might actually be worth just having some sort of conditional expression in the JMESPath language. I think that's a common enough request. Either via if/else statements, or possibly with something like C's ternary operator.

@meric
Copy link
Author

meric commented Oct 31, 2015

Is there anything I can do to assist in pursuing these objectives? I'd love to help if I can.

@arthurprs
Copy link

I'd love to have a way to evaluate boolean expressions, this is the type of hack I'm resorting to [geo.country][?@=='UK']

@meric
Copy link
Author

meric commented Feb 8, 2016

Could we perhaps implement it in Options? Feed through functions in a dictionary or class with static attributes there.

@jamesls
Copy link
Member

jamesls commented Feb 23, 2016

I've pushed a branch that adds support for adding custom functions via Options as suggested, but the API is a little too low level for me to merge. The RuntimeFunctions class was not designed as a public facing API so I need to restructure some of the internals first.

That API I'm shooting for is you can just subclass from a base class and anything with a function signature (and likely a naming convention) will be registered as a jmespath function. Let me play around with a few ideas.

jamesls added a commit to jamesls/jmespath that referenced this issue Feb 23, 2016
jamesls added a commit to jamesls/jmespath that referenced this issue Feb 23, 2016
@jamesls
Copy link
Member

jamesls commented Feb 23, 2016

I ended up finding an API that seemed reasonable to me, and still allowed you to leverage the type checking/arity checking that built in jmespath functions have.

Let me know if you have any feedback: #102

@rageycomma
Copy link

rageycomma commented Jan 2, 2021

Just in case this is useful for anyone, I found a way to do it (sorry for posting on a dead issue but it's #1 result on google..)

Background: I have a field which is titled "Facebook" but sometimes contains VK URLs due to an input error. I wanted to do something like:

if contains vk.com
name = VKontakte 
else 
name = Facebook

To accomplish that, I'm making a custom group from data, where Facebook is a property on an object like this:

{
"Facebook": "http://vk.com/blahblah"
}

I want to create an object like this when it contains Facebook:

{
"name": "VKontakte",
"value": "http://vk.com/blahblah"
}

And like this when it contains Facebook:

{
"name": "Facebook",
"value": "http://www.facebook.com/blahblah"
}

And the way I accomplished that was:

thisProp: [
 { "name": 'VKontakte', "display": contains(Facebook,'vk.com'), "value": Facebook },
 { "name": 'Facebook', "display": contains(Facebook, 'facebook.com'), "value": Facebook},
][?display]

Then finally remove the field you just created to check it:

thisProp: [
 { "name": 'VKontakte', "display": contains(Facebook,'vk.com'), "value": Facebook },
 { "name": 'Facebook', "display": contains(Facebook, 'facebook.com'), "value": Facebook},
][?display].{ "name": name, "value": value }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants