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

How could jsonata get matched key name? #83

Closed
wsd1 opened this issue Oct 21, 2017 · 8 comments
Closed

How could jsonata get matched key name? #83

wsd1 opened this issue Oct 21, 2017 · 8 comments

Comments

@wsd1
Copy link

wsd1 commented Oct 21, 2017

sample data:

{
  "docId1": {
      "tags":["sales", "markting", "house"],
      "name": "How to sale house."
    },

  "docId2": {
      "tags":["tech", "smart home", "house"],
      "name": "How to make house smart."
    }
}

I want select doc by tag, returns like:

[
  {
    "name": "How to sale house.",
    "key": "docId1"
  },
  {
    "name": "How to make house smart.",
    "key": "docId2"
  }
]

My try is:

$.*["house" in tags].{"name": name, "key": ? }

I don't know how to reference wildcard key's name? Can you help me?

By the way, I know "$each" function is the answer. I just want to know is there some operators can fix my situation.

Thanks a lot.

@shrickus
Copy link

shrickus commented Oct 23, 2017

The function you are looking for is $lookup(object, key)

For your sample data input, this expression returns what you want:

$keys().{
    "name": $lookup($$, $).name,
    "key": $
}

Alternately, you can use the $spread(object) function to create separate object from each field of the original object. Then to get the original field string, use $keys(object) and grab the first element of the 1-element array of keys:

$spread().{
    "name": *.name,
    "key": $keys()[0]
}

This is another one of those cases where the expression would be greatly simplified if only there was a way to refer to the parent (or more generally, any ancestor) object. I think the problem is that the lambda closures don't have a way to access the object/function that called them -- Andrew may be able to explain again why this is not possible.

@andrew-coleman
Copy link
Member

Yeah, I sympathise with the desire to access the 'parent'. It's so easy to do in XPath, so why not JSONata? Let me try and explain:

When processing XML data, XPath is working against a DOM tree which is a fully interconnected set of nodes, each of which has direct access to first child, last child, previous sibling, next sibling and parent. Also, each node has a name and a value, so even if you navigated to a node using a wildcard, you still have access to its name.

JSON is a much simpler data structure. There is no concept of nodes; a value itself doesn't have a name even though an object is a set of name/value pairs; nor is there any connection between values. Sure, I could have designed the language to 'parse' the input JSON into the equivalent of a DOM tree, but that would have made the language and its implementation far more complex, and I was striving for simplicity.

I've spent a lot of time thinking of how to simplify the sort of transformation that you are trying to do here, and I'll continue to scratch my head on this.

@Allam76
Copy link

Allam76 commented Oct 30, 2017

A while ago I worked with an xpath like parser called bpath in SAP ABAP.

There existed the concept "framework access class". This class was called every time the parser moved up or down the path and by using this intelligently the parent could be attached to the current object and later retrieved. I experimented with this concept in JS and this concept is quite trivial.

The added benefit is that also non JSON data, such as db data can be used simply by swapping the framework object

So if anyone is interested I could elaborate somewhere.
Cheers
Martin

@andrew-coleman
Copy link
Member

@Allam76 I'm interested

@Allam76
Copy link

Allam76 commented Oct 31, 2017

OK, I'm travelling today, hopefully I can clean and present a small version tomorrow.

@Allam76
Copy link

Allam76 commented Nov 2, 2017

I put the framework access class from the other project on my fork framework-access

This solution has a parser just like jsonata, I did not publish that code since it is very ugly. The parser parses a syntax similar to xpath. Every time the parser determines that something moves on the path, a call is delegated to the framework access class. This class has three types of methods:

  1. Everything that ends with name, these are for code completion.
  2. All others, they are used to set a current object, manipulating collections, parent, children etc.
  3. Instantiation methods, useless in a js context.

These methods in 2) are similar to the evaluate* functions in jsonata.

To translate this to jsonata, one would need to break out the evaluate* functions in a separate file, add them to the parser and allow them to be overridden. Inside one would also need to capture the current object, depending on where the parser is in the path at that moment. Maybe that could be realized with a $current variable and/or $parent variable?

Lastly one must with this approach accept side effects, typically only to set the parent. Please also note that this implementation comes from a class based language, much less flexible than js. Therefore the code is sub optimal for our use case. We can without doubt figure something out that is much more clever for our scenario.

@Allam76
Copy link

Allam76 commented Nov 2, 2017

I'm super busy at the moment but when I have time I could try to do the following:

  1. Put evaluate* in a seperate file to allow to be overridden.
  2. Add $parent and $current variables.

This could potentially solve this issue and the mixin issue at the same time.

With this, we have the benefit to use jsonata to transform a lot more than json. I personally use it with postgresql relational DB.

@andrew-coleman
Copy link
Member

Closing, since discussion has ceased.

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

No branches or pull requests

4 participants