ohler55 / ojg

Optimized JSON for Go
MIT License
857 stars 49 forks source link

Parsing a filter expression using Equation #98

Closed juliannguyen4 closed 1 year ago

juliannguyen4 commented 1 year ago

I'm trying to parse the JSONPath $[?(@.price < 10)] in code by passing it to:

expr := jp.MustParseString("$[?(@.price < 10)]")

and reading each fragment in expr. But I'm unable to parse the filter expression in the path.

I know that filter expressions are first declared as Equation objects in code and passed into Filter objects, and the Filter objects encode these Equation objects in an internal data structure. But so far, I haven't found a way to convert the Filter objects back to Equation objects. Is there a way to do this?

ohler55 commented 1 year ago

I see the confusion, Equation instances are used to build a Script if building by hand. After setting up an Equation the Script() function is called to return a Script or Filter() is called to return a Filter. You can't go the other way around from Filter to Equation. The end goal is to produce a Filter or Script and Equation is just one of the ways to do that.

juliannguyen4 commented 1 year ago

Hi @ohler55, would it be possible to make an Equation accessible from a Filter?

In my code I'm iterating through each Frag in the Expr and I would like to add support for filter expressions, and the Frag type that contains a filter expression is *Filter. But I'm unable to access the filter expression (via Equation) programmatically through a Filter object.

I'm also willing to make a PR to support this, if you're OK with that.

ohler55 commented 1 year ago

I'm not sure using the Equation type is the best way to view the Filter internals. Can you provide a bit more on what it is you are trying to do the the Filter? Let's see if we can come up with something.

juliannguyen4 commented 1 year ago

Hi @ohler55 , sorry for the late reply. I'll try my best to explain.

I'm currently developing an API that converts JSONPath strings into Aerospike server operations, and I'm doing this for Aerospike's Golang client. The way the API currently works is:

  1. It takes in JSONPath strings through an API call
  2. It parses the JSONPath string using the jp library into an Expr object
    expr, err := jp.ParseString(jsonPath)
  3. Then, one by one, it reads each Frag in the expr (which is basically a list of Frag objects). It determines which kind of operation it is and translates it to a server operation. For example, if the frag has a concrete jp.Nth type, we would translate that to a server operation that gets a list item at a specific index.

The problem is that filter expressions are encoded as Filter objects (which is also a Frag), but there's no way to access the expression itself for a Filter object. If there's a way to access the Equation of a filter such as this, we would easily be able to convert it to the equivalent server operation.

ohler55 commented 1 year ago

I see. Right now you can get the string representation of the filter but then you would have to parse the string which would be a pity since the filter already has the info. Let's look at what would be the ideal solution first then see what can reasonably be done.

If a call was added to Filter and Script (might as well) that returned some kind of struct or slice of structs that described the Script/Filter I think that would be best for your situation. The struct should include the left side, right side, and operation. Something like:

type Foo struct {
    op   string
    left any
    right any
}

What do you think?

juliannguyen4 commented 1 year ago

That looks good for binary operations. For unary operations like getting an item from a map (i.e @.key), we can set the right attribute to be nil.

ohler55 commented 1 year ago

Exactly. That's what OjG does.

ohler55 commented 1 year ago

I started a branch named "inspect-filters". I'll be working in that branch to add this feature.

ohler55 commented 1 year ago

I have an implementation but I need more tests. Please give it a try.

Testing complete but could use some feedback about whether it works for you.

juliannguyen4 commented 1 year ago

Hi @ohler55 , thank you so much for getting back to me. I'll check if my code works with yours

ohler55 commented 1 year ago

Please let me know if the addition works for you.

juliannguyen4 commented 1 year ago

Hi @ohler55, I have to put my project on the backlog for a few weeks, but I will continue working on this at the start of next month. Thanks for helping me out by adding this feature!!

ohler55 commented 1 year ago

Ok, I'll merge and release anyway.

ohler55 commented 1 year ago

v1.15.0 released