Galbar/JsonPath-PHP

"in" operator with subexpression only taking first result

ElandaOfficial opened this issue · 3 comments

Hello there :D,
I am using your library for querying my JSON data.
It is all lovely and such, however I came across an issue when dealing with more complex in expressions.

Let's assume I have a json document such as (this is test data I use to model the query):

[
	{
		"fqn": "something"
	},
	{
		"fqn": "somethingelse",
		"refs": [
			"something1",
			"something6"
		]
	},
	{
		"fqn": "something1"
	},
	{
		"fqn": "something2"
	},
	{
		"fqn": "something3"
	},
	{
		"fqn": "something4"
	},
	{
		"fqn": "something5",
		"refs": [
			"something7"
		]
	},
	{
		"fqn": "something6"
	},
	{
		"fqn": "something7"
	}
]

What I want now, is to receive all objects which fqn is not in a refs block of another object.
This works... kind of with the following query: $[?(@.fqn in [$.*.refs.*])]

However the issue at hand here is that the subexpression $.*.refs.* will actually return ["something1", "something6", "something7"], which is what I want, the issue though is that, since the Value class only ever returns the first element, which in this case is something1, it will only ever output:

[
    {
        "fqn": "something1"
    }
]

Is there a different way to achieve what I want?
Elanda

Edit:
I am really sorry to nitpick here, but I must understand something.
I generally come to wonder why subtreeGet returns the result as an array, when a json object can be anything an array, an object, a float ect., wouldn't it make more sense that it would return the actual object instead of an array with a single object?
Semantically speaking, that's how json works.
I know that path expressions can return more than one match, but the rest is discarded by Value anyway.

Hello, sorry I didn't reply earlier, for some reason I didn't see the notification 😓

The issue you are facing is, in a way, by design. This doesn't mean that it should be kept like this, though. It just means that it is working as intended. Let me explain:

k in [x, y, z] literally means is k inside of the "container" containing x, y and z. It is intended for k, x, y and z to be specific values (hence the use of Value). If they happen to be a path to multiple values, the first is taken (it could have been a random one, but we chose the first one for consistency) as Value represents a single value.

The syntax you'd hypothetically want is something like value 'in' (jsonpath | childpath) (i.e. 3 in $.path.to.numbers). Notice the lack of [] around it. Meaning that the "container" is the results of the path. This is currently not implemented as part of this library, though. If you want to implement it, I am open to reviewing a PR for it.

I don't think there is currently a native way of doing what you want to achieve. The alternative would be to first query for $.*.refs.* and then build a query with the results such as $[?(@.fqn == $result[0] or @.fqn == $result[1] or ...)].

PS: subtreeGet returns array($result, $hasDiverged). $result is an array of "things that matched the given path", the contents of which are the actual values (int, float, string, array, etc.). subtreeGet is used in other places outside of Value, where all the results are considered.

Thank you for the answer, I appreciate it a lot.

For the part with the pull request, I have implemented the JSONPath draft RFC 8259 for myself in PHP now and since this seems to be a niche issue I've had, I guess not many people will run in the same issue as me anyway, otherwise I would've been glad to contribute. :)

Have a nice day!

No problem.

I wasn't aware of the RFC. Thanks for sharing it.

Cheers!